Skip to content

Dataface Warning Reference

Auto-generated from dataface.core.diagnostics.REGISTRY. Every WARN-* code Dataface can emit, grouped by docs topic. All warnings are suppressible via a query's ignore: or a chart's warnings_ignore: field.

charts

WARN-BAR-BAND-WIDTH-TOO-NARROW — Bar chart bands are too narrow to read

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Chart {chart_id!r} has {distinct} bands across {render_width:.0f}px (~{band_width:.2f}px each); below {min_band_width:.0f}px the fill disappears under the bar's own stroke.

Fix: Widen the chart, reduce the number of categories, or (for time series) roll up to a coarser grain (e.g. day -> week or month).

Fires when a (vertical) bar chart packs so many bands into its plot width that each band's fill drops below a readability floor — the fill disappears and the bar's own border stroke merges neighbours into a "ghost band" smear. Classic trigger: daily-granularity data (hundreds of distinct days) rendered as bars at a normal chart width.

WARN-LAYERED-CHART-SHARED-Y-AXIS-SCALE-MISMATCH — Layered chart y series have a large scale mismatch

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Chart {chart_id!r}: y columns {col_a!r} and {col_b!r} share a y-axis but their value ranges differ by {ratio:.0f}× — the smaller series will be visually crushed to a flat line.

Fix: Split into two y-axes by adding axis_y: on one of the layers, or normalize the series to a common scale in the query.

Fires on a layered chart where the base chart's own y series and/or its layers share the y-axis but their value ranges differ by ≥100× — the smaller series is visually crushed to a flat line. Classic example: revenue (millions) overlaid with conversion rate ([0, 1]).

WARN-LIKELY-CURRENCY-OR-PERCENT-MISSING-FORMATTER — Y-axis field looks like money or a percentage but uses a generic format

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Chart {chart_id!r}: field {field!r} looks like {kind} but the y-axis format is {format!r}.

Fix: Set style.axis_y.format to a currency format (e.g. $,.2f) or a percent format (e.g. .1%) to match the field's meaning.

Fires when a chart's y-encoding field name looks like money or a percentage but the chart's baked y-axis format is unfit to render that kind. Detection is name-based: fields ending in _usd, _revenue, _amount, _pct, _rate, etc. trigger when the resolved y-axis format does not carry $ or %.

WARN-PALETTE-UNSUPPORTED — Palette name is a known anti-pattern

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Chart {chart_id!r}: palette {requested!r} is a known anti-pattern; resolved to {resolved} instead.

Fix: Author a supported palette name — see the anti-patterns table in docs/guides/palette-resolver.md#anti-patterns.

Fires when a chart authors a palette name on the known anti-pattern list (e.g. 'RdYlGn', 'parula') — these are CVD-hostile or superseded by a DFT-native palette. palette() resolves the substitute silently at compile time; this detector is the only place the nudge surfaces.

WARN-PIE-DOMINANT-SEGMENT — Pie chart is dominated by a single segment

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Pie chart {chart_id!r}: {dominant_field!r} holds {dominant_share:.0%} of the total — the chart conveys a single value.

Fix: Use a KPI chart for the dominant share and a bar or table for the breakdown, rather than a pie dominated by one slice.

Fires on pie/donut charts where one slice is so large that the chart conveys a single value — the other slices are visually negligible. A near-single-value pie should be a KPI (the dominant share) plus a breakdown elsewhere.

WARN-PIE-TOO-MANY-SEGMENTS — Pie has too many slices to read

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Pie chart {chart_id!r} has {segment_count} segments; angles are hard to compare past {max_segments} slices.

Fix: Use a bar chart sorted by value, or group small segments into 'Other'.

Fires on pie/donut charts whose query returns more segments than a reader can compare by angle. Humans judge angle poorly past a handful of slices; a pie with many segments is unreadable and should be a sorted bar chart.

WARN-POINT-MAP-OUT-OF-PROJECTION — Point map has data outside the projection boundary

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Point map {chart_id!r}: {dropped_count} of {total_count} points are outside the {projection!r} projection boundary and were dropped.

Fix: Filter the data to the projection's region, or switch to a projection that covers the full data extent.

Fires when a point_map chart uses a bounded projection (e.g. albersUsa) and some data points fall outside its mapped region. The emitter drops those rows from spec.data before Vega-Lite sees them; this warning reports how many points were dropped and why.

WARN-QUERY-RETURNED-ZERO-ROWS — Chart query returned zero rows

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Chart {chart_id!r}: query returned zero rows.

Fix: Check the WHERE clause or date filter — it may be excluding all data for the current filter values.

Fires on any chart whose query returned zero rows. An empty chart renders as a blank panel with axes — no signal to the viewer that the query returned nothing. Most common cause: a WHERE clause or date filter that excludes all data.

WARN-REDUNDANT-ENCODING — Same column bound to two visual channels

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Chart {chart_id!r}: field {field!r} is bound to channels {channels} — binding the same field twice adds no information.

Fix: Remove one of the channel bindings, or use different fields for each channel to encode distinct dimensions.

Fires when one query column is bound to two or more visual channels of the same chart (e.g. y and color both set to the same field). Binding the same field twice adds no information — the second channel is redundant. The bar x==color case is excluded: it renders full-width category-colored bars, which is a useful pattern.

WARN-TABLE-COLUMNS-OVERFLOW — Table is wider than its dashboard slot

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Table {chart_id!r} overflows its slot: needed {needed_width:.0f}px but only {available_width:.0f}px available.

Fix: Widen the table's dashboard slot, reduce the number of columns, or add explicit column widths to control how the table distributes its available space.

Fires when a table needs more width than the slot it was given. A table sizes each column to its minimum readable width; when those widths sum past the available width, the renderer widens the whole table past its slot — so in a dashboard it spills over its neighbour or is clipped, printing columns on top of each other.

WARN-TEMPORAL-SINGLE-POINT — Temporal line or area chart has only one data point

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Chart {chart_id!r} ({chart_type}): temporal x-axis has exactly one data point — a one-point line/area conveys no trend.

Fix: Widen the date filter to include more time periods, or switch to a KPI or stat tile if a single-point value is intentional.

Fires on line and area charts where the x-axis is temporal and the query result has exactly one row. A one-point line is rendered as a single dot; a one-point area is a vertical line. Both render but convey nothing about a trend — this almost always means the date filter is too narrow.

WARN-TOO-MANY-COLOR-CATEGORIES — Color encoding has more categories than the palette can distinguish

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Chart {chart_id!r}: color field {field!r} has {count} distinct values — the palette only has {max_categories} distinct colors before recycling.

Fix: Reduce the number of color categories by grouping small values into 'Other', or filter the data to the most significant categories.

Fires when a categorical color encoding has more distinct values than the palette can distinguish — colors recycle and the legend becomes unreadable. Gated on the Vega-Lite color encoding type so a continuous (quantitative) color gradient never trips it.

WARN-TOO-MANY-X-CATEGORIES — Categorical x-axis has too many distinct values to read

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Chart {chart_id!r}: x field {field!r} has {count} distinct values — labels collide and marks are too thin to read (limit: {max_categories}).

Fix: Filter to the top N categories by value, roll up to a coarser grouping, or switch to a scrollable table for wide categorical data.

Fires when a categorical (nominal/ordinal) x-axis has more distinct values than fit legibly — labels collide and the marks are too thin to read. Gated on the Vega-Lite x encoding type so it never fires on a temporal or quantitative axis.

WARN-UNREFERENCED-CHART — Chart is defined but not placed in any layout

  • Level: warning
  • Domain: compile
  • Suppressible: yes

Message template:

Chart {chart_id!r} is defined but not referenced in any layout (rows/cols/grid/tabs). It will not appear in the rendered dashboard.

Fix: Add the chart to a layout block, or delete it if it is no longer needed.

Fires at compile time for charts that are defined somewhere in the face tree but never placed in any layout (rows/cols/grid/tabs). The face still compiles because content-only faces are valid; this warning surfaces lazy authoring — an author defined a chart and forgot to display it. Emitted by compile/face_warnings.py.

WARN-VALUE-LABELS-CROWD-WIDTH — Value labels are wider than their per-mark slot

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Chart {chart_id!r}: widest value label is {label_width:.0f}px but each mark only has {slot_width:.0f}px — labels will overflow and collide with neighbours.

Fix: Shorten the number format (e.g. use SI suffix .2~s instead of full precision), reduce the number of labeled marks, or widen the chart.

Fires when a chart's value labels are wider than the horizontal room each one gets. Value labels are drawn at the mark, fixed size, with no adaptive avoidance, so they are the label kind that genuinely overflows. The check uses the panel's real rendered width and font metrics — it fires exactly when the widest label is wider than its slot.

WARN-Y-ENCODING-MOSTLY-NULL — Y-encoding field is mostly NULL in the query result

  • Level: warning
  • Domain: render
  • Suppressible: yes

Message template:

Chart {chart_id!r}: y field {field!r} is {null_pct:.0%} NULL across {row_count} rows.

Fix: Check for a broken join or a nullable source column. A COALESCE or WHERE clause may be needed to filter the empty rows.

Fires on any chart where the y-encoding field is more than 50% NULL in the query result rows. Mostly-empty visual marks with no explanation usually indicate a broken join or a nullable source column. NULL-only differs from NULL+zero: zero is a valid measurement.

face

WARN-DOUBLE-HEADER — Face title is repeated by a heading at the top of the body

  • Level: warning
  • Domain: compile
  • Suppressible: yes

Message template:

Face title {title!r} is followed immediately by a level-{level} markdown heading {heading!r} — the board renders both, so the dashboard headers itself twice.

Fix: Delete the heading line from the body text — title: already renders as the board header — or drop title: and let the markdown heading be the header.

Fires when a face sets title: and the first block of its body markdown is a heading that either is level 1 (a document has one document title) or repeats the face title. The board prints the face title as its header and the markdown heading directly beneath it. A heading further down the body, or a lower-level heading with different text (## Overview under title: Sales), is ordinary section structure and does not fire. Heading detection uses the same markdown parser that renders the text, so a fenced code block opening with a # comment is not mistaken for a heading. Checked on the board itself, not on nested faces — a section card pairing its title with a styled body heading is a deliberate pattern. Emitted by compile/face_warnings.py.

WARN-H1-BODY-NO-TITLE — Body opens with a level-1 heading but the face has no title

  • Level: warning
  • Domain: compile
  • Suppressible: yes

Message template:

Body opens with a level-1 markdown heading {heading!r} and the face has no `title:` set — the heading renders as plain body text instead of the styled board header.

Fix: Set title: {heading} and delete the # {heading} line from the body text.

Fires when a face has no title: and the first block of its body markdown is a level-1 heading — the author likely reached for a markdown heading instead of the purpose-built title: field, so the board renders as unstyled prose with no header. Only a literal level-1 heading counts; a lower-level heading (## Overview) with no title is ordinary section structure and does not fire, since there is no title text to compare it against. Heading detection uses the same markdown parser that renders the text, so a fenced code block opening with a # comment is not mistaken for a heading. Checked on the board itself, not on nested faces — a section card's own opening heading is not inspected. Emitted by compile/face_warnings.py.

WARN-SINGLE-CHART-REDUNDANT-TITLE — Single-chart dashboard has both a face title and a chart title

  • Level: warning
  • Domain: compile
  • Suppressible: yes

Message template:

Face title {title!r} sits above a single chart that carries its own title {chart_title!r} — two headers for one chart.

Fix: Give the board one header: drop the chart's title: (the face title already heads the board, and it is what listings, search, and nav show), or drop the face title: and let the chart title stand.

Fires when a face's whole content is one chart and both the face and the chart carry a title, so the board stacks two headers over a single piece of content. A single-chart dashboard does not need a separate dashboard title. Does not fire when only one of the two is titled, when the face holds more than one chart, or when body prose sits between the two titles. KPI and callout charts are excluded — a KPI labels itself with label: and a callout's title is a prose lead-in, so neither stacks a chart header under the board's. Checked on the board itself, not on nested section faces. Emitted by compile/face_warnings.py.

layout

WARN-FLAT-COLS-UNSIZED-OVERFLOW — Flat cols: row has too many unsized non-KPI cells

  • Level: warning
  • Domain: compile
  • Suppressible: yes

Message template:

cols: has {growable_count} unsized non-KPI cells (max recommended {max_unsized}). Each inherits the row's full vertical budget, so a wide flat row makes row height unpredictable. KPI cards are height-stable and don't count.

Fix: Nest cells into sized rows-in-cols groups instead, e.g.: cols: - width: "17%" rows: - kpi_a - kpi_b - width: "55%" rows: - hero

Fires when a flat cols: row has more unsized non-KPI cells than the recommended limit. Each unsized cell inherits the row's full vertical budget, making row height unpredictable with many columns. KPI cards are carved out because they are height-stable. Emitted by compile/authoring_warnings.py.

queries

WARN-FANOUT-RISK — Join may multiply rows beyond chart aggregation

  • Level: warning
  • Domain: query
  • Suppressible: yes

Message template:

Query {query_name!r}: {message}

Fix: Add a GROUP BY or aggregation in the query to collapse the duplicate rows before they reach the chart.

Fires when query validation detects a join that may multiply rows beyond what the chart's aggregation can recover. When a cached relationship context (super-schema profiles) is available, severity is calibrated against known multiplicities. Emitted by validate_compiled_queries indirectly via dataface_super_schema.inspect.relationship_warnings.

WARN-MISSING-JOIN-PREDICATE — Join is missing a predicate and may produce a cross join

  • Level: warning
  • Domain: query
  • Suppressible: yes

Message template:

Query {query_name!r}: {message}

Fix: Add an ON clause to the join to specify how the tables relate.

Fires when query validation detects an implicit cross join or an explicit CROSS JOIN without a predicate. These almost always indicate a missing ON clause and produce wildly fanned-out result sets. Emitted by validate_compiled_queries via the query validator.

WARN-PARSE-ERROR — SQL query could not be parsed for semantic validation

  • Level: warning
  • Domain: query
  • Suppressible: yes

Message template:

Query {query_name!r}: {message}

Fix: Check the SQL syntax. The query may still execute, but semantic validation (fanout detection, reaggregation) cannot run on unparseable SQL.

Fires when a SQL query cannot be parsed as a structured AST. The query may still execute against the warehouse — this warning surfaces that semantic validation (fanout, reaggregation) cannot run on unparseable SQL. Emitted by compile() for authored queries whose dialect resolves and whose failure carries a position, and by validate_compiled_queries via the query validator.

WARN-REAGGREGATION — Aggregation applied on top of an already-aggregated input

  • Level: warning
  • Domain: query
  • Suppressible: yes

Message template:

Query {query_name!r}: {message}

Fix: Refactor the query to aggregate only once at the correct level, or verify the double-aggregation is intentional.

Fires when query validation detects aggregation applied on top of an already-aggregated input (e.g. SUM(SUM(...)) patterns or aggregation over a query result that itself aggregates). The result is usually not what the author intended. Emitted by validate_compiled_queries via the query validator.

variables

WARN-REDUNDANT-AUTHORED-DEFAULT — Variable field is explicitly set to its default value

  • Level: warning
  • Domain: compile
  • Suppressible: yes

Message template:

Variable {var_name!r} explicitly sets {field_name} to the default value {default_value!r}.

Fix: Omit {field_name}: when the default is intended.

Fires when a variable explicitly sets a field to its default value. The explicit setting is redundant and adds noise without changing behavior. Emitted by compile/authoring_warnings.py.

WARN-REDUNDANT-AUTHORED-LABEL — Variable label is the same as the inferred display name

  • Level: warning
  • Domain: compile
  • Suppressible: yes

Message template:

Variable {var_name!r} sets label to {authored_label!r}, which is already inferred from the variable name.

Fix: Omit label: unless the display label should differ from the inferred name.

Fires when a variable's authored label: is exactly the same as the label Dataface would infer from the variable name (title-cased, underscores to spaces). The explicit label is redundant and can be omitted. Emitted by compile/authoring_warnings.py.