{# Vendored from opportunities/_table.html. Same wrapper, same ui-table-head-row + ui-table-row grammar, same click-to-sort header markup, same ui-signal-chip + ui-legend-dot chip styles. Deltas vs the opportunities table: - column set swaps "Issue" → "Reason", "Query Cost" → "Slot Wasted", "Last Detected" → "Last Failed", and drops "Partition / Cluster" (irrelevant on failures); - row click target → /failures/{hash_key}; - empty-state copy mirrors opportunities. #}
{% from "components/_sort_header.html" import sort_header %} {# signal_chip hoisted out of the per-cell conditional below so it stays available across sibling {% if %} blocks. Jinja {% from %} scope is the enclosing block; importing inside one cell's {% if %} left later cells (Workload, Suggestions) referencing an undefined macro when last_error_reason was falsy. #} {% from "components/_signal_chip.html" import signal_chip %} {% if rows and rows | length > 0 %} {% set _qs = pagination.query_string_without_sort_or_page %}
{{ sort_header("dataset", "Dataset", signals.sort, signals.direction, base_qs=_qs) }} {{ sort_header("table", "Object", signals.sort, signals.direction, base_qs=_qs) }} {{ sort_header("author", "Author", signals.sort, signals.direction, base_qs=_qs) }} {{ sort_header("reason", "Reason", signals.sort, signals.direction, base_qs=_qs) }} {{ sort_header("workload", "Workload", signals.sort, signals.direction, base_qs=_qs) }} {{ sort_header("slot", "Slot Wasted", signals.sort, signals.direction, align="right", base_qs=_qs, tooltip="Sum of total_slot_ms across every failed execution of this query body.") }} {{ sort_header("age", "Last Failed", signals.sort, signals.direction, align="right", base_qs=_qs) }} {{ sort_header("suggestions", "Suggestions", signals.sort, signals.direction, align="center", base_qs=_qs) }} {% for row in rows %} {% endfor %}
{{ row.affected_dataset }} {{ row.affected_object }} {% if row.author_email %} {{ row.author_email }} {% else %} - {% endif %} {% if row.last_error_reason %} {# Visible label is the humanised reason ("Invalid query" instead of "invalidQuery"); tooltip shows the live BigQuery error message when present, else the curated description, else the raw camelCase code so it stays grep-discoverable on hover. The Reason filter dropdown keeps the raw codes as the filter values for matching. #} {{ signal_chip( row.last_error_reason | bq_reason_humanize, color="rose", title=row.last_error_message or (row.last_error_reason | bq_reason_description) or row.last_error_reason ) }} {% else %} - {% endif %} {% if row.query_workload_kind == 'build' %} {{ signal_chip("Build", color="brand", title="This failed query body materialises data (CTAS, MERGE, INSERT, UPDATE, DELETE, TRUNCATE).") }} {% elif row.query_workload_kind == 'consumption' %} {{ signal_chip("Consumption", color="teal", title="This failed query body reads data (SELECT).") }} {% else %} {{ signal_chip("Unclassified", color="slate", title="Workload couldn't be determined from this failed query's statement type.") }} {% endif %} {{ row.slot_wasted_human }} {{ row.last_detected_at.strftime("%d/%m/%Y") if row.last_detected_at else "-" }} {% set _sugg = row.table_suggestion_count or 0 %} {% if _sugg > 0 %} {{ signal_chip(_sugg ~ " suggested", color="emerald", title=_sugg ~ " suggestion rule(s) fired across this query body - open the row to see each diff") }} {% endif %}
{# Failures listing groups by query_hash (one row per failed query body) so the unit is "failed queries". #} {% with pagination_item_noun = "failed queries" %} {% include "components/_pagination.html" %} {% endwith %} {% else %}

No failures match your filters

{% endif %}