Summary
Since #2501 wired dashboard-level filters — including the built-in dateRange picker (reserved filter name dateRange) — into every widget's analytics query, a dashboard whose filter field does not exist on a given widget's underlying object emits invalid SQL and the widget crashes at render time. This is a build-time-decidable invariant that currently escapes the entire static gate (os validate + os build) and only fails when a user opens the dashboard.
Surfaced upgrading a production app (HotCRM) from 14.7 to 16.0.0-rc.1.
Repro
A dashboard binds dateRange: { field: 'close_date' }. close_date exists only on the opportunity object; widgets on other objects (account / contact / lead / product) inherit the filter and produce:
SELECT COUNT(*) AS "contact_count" FROM "crm_contact"
WHERE close_date >= '2026-07-01' AND close_date <= '2026-09-30'
-- SqliteError: no such column: close_date (service-analytics executeRawSql)
os build / os validate pass; the error only appears at query time (logged-in user opens the dashboard), rendering the widget as an error card.
Root cause
os validate already runs "Checking dashboard widget bindings (ADR-0021)", but it validates the widget → dataset binding only — it does not check that a dashboard-level filter's field exists on each widget's dataset object. After #2501 those filters are applied to the widget query, so their field refs became a new class of runtime-only failure.
Proposed check (extend ADR-0021)
For each dashboard filter (dateRange + every globalFilters[]), for each widget, error unless:
- the filter's effective field (after
filterBindings re-target) exists on the widget's dataset object, or - the widget opts out via
filterBindings: { <filterName>: false }.
This is the same field-existence invariant ADR-0032 already enforces for formula / sharing-rule CEL references — dashboard filter fields are the same category, just not yet covered.
Acceptance criteria
os build / os validate fails a dashboard where a filter field is absent on a bound widget's object and the widget has no opt-out, with a message naming the dashboard, widget, filter, field, and object, e.g.:
executive_dashboard: widget total_accounts inherits dateRange(close_date), but object crm_account has no field close_date — set filterBindings: { dateRange: false } or re-target to an existing field.
- A widget with
filterBindings: { <name>: false } (or a re-target to an existing field) passes. - Fixture coverage for both the failing and opted-out shapes.
Happy to send a PR — the check is well-scoped and mirrors the existing ADR-0032 field-ref validator.
Related shift-left checks from the same 14.7 → 16 upgrade: #3365, #3366, #3367.
Summary
Since #2501 wired dashboard-level filters — including the built-in
dateRangepicker (reserved filter namedateRange) — into every widget's analytics query, a dashboard whose filter field does not exist on a given widget's underlying object emits invalid SQL and the widget crashes at render time. This is a build-time-decidable invariant that currently escapes the entire static gate (os validate+os build) and only fails when a user opens the dashboard.Surfaced upgrading a production app (HotCRM) from
14.7to16.0.0-rc.1.Repro
A dashboard binds
dateRange: { field: 'close_date' }.close_dateexists only on the opportunity object; widgets on other objects (account / contact / lead / product) inherit the filter and produce:os build/os validatepass; the error only appears at query time (logged-in user opens the dashboard), rendering the widget as an error card.Root cause
os validatealready runs "Checking dashboard widget bindings (ADR-0021)", but it validates thewidget → datasetbinding only — it does not check that a dashboard-level filter's field exists on each widget's dataset object. After #2501 those filters are applied to the widget query, so their field refs became a new class of runtime-only failure.Proposed check (extend ADR-0021)
For each dashboard filter (
dateRange+ everyglobalFilters[]), for each widget, error unless:filterBindingsre-target) exists on the widget's dataset object, orfilterBindings: { <filterName>: false }.This is the same field-existence invariant ADR-0032 already enforces for formula / sharing-rule CEL references — dashboard filter fields are the same category, just not yet covered.
Acceptance criteria
os build/os validatefails a dashboard where a filter field is absent on a bound widget's object and the widget has no opt-out, with a message naming the dashboard, widget, filter, field, and object, e.g.:filterBindings: { <name>: false }(or a re-target to an existing field) passes.Happy to send a PR — the check is well-scoped and mirrors the existing ADR-0032 field-ref validator.
Related shift-left checks from the same 14.7 → 16 upgrade: #3365, #3366, #3367.