Skip to content

Reference

Errors

The publish finding envelope and its layers, every semantic check code, the refresh failure classes, and the named refusals you will meet.

Dashies refuses in three different places, and which one you hit tells you what to do about it.

WhereWhenWhat survives
PublishBefore anything is writtenNothing was published. Fix and republish.
RefreshOn a schedule, unattendedThe last good body stays live. The run is recorded as failed.
The published pageWhile a reader is looking at itThe tile states a reason in place. See Honest degradation.

The publish finding envelope

A failed publish reports every finding at once, in a fixed shape:

Publish failed - 2 errors (nothing was published):
- [semantic] /datasets/revenue/measures/arr: measure `arr` divides one aggregate by another ...
- [compile] /tiles/3/measure: a pie cannot display a "cents"/"points"-scaled measure ...

Warnings use a separate line and do not stop the publish:

warning: [compile] /datasets/revenue/sql: ...

On a publish that succeeds, warnings arrive grouped rather than one line each: warnings to review first, then the checks you owe, then routine notes. The same warning on several datasets prints once, with a Same for datasets: line naming the rest. A dataset whose data stays with Dashies gets one note in place of the separate warnings about it (two on a page of managed tiles, three on a page whose markup you wrote), and a publish whose First data: line says its rows are already served drops the wait from that note, which leaves no note at all on a page of managed tiles. A publish that passes a dry run's spec_hash and report_id counts the warnings that dry run printed or counted, on a line that opens Since the dry run of, instead of printing them again; the publish_dashboard reference says when that applies. The JSON part of the response always lists every warning.

The bracketed word is the layer, and the path is a JSON pointer into your spec.

LayerWhat it means
argA tool argument is wrong: the path, the metadata, a spec_edits entry.
identityThe document does not name the dashboard you are publishing to: a slug mismatch, or a look.from that does not resolve.
steerThe dataset mode could not be resolved and you have to declare it.
semanticThe spec is well-formed but the seeded data contradicts what it declares.
compileThe spec cannot be turned into a page, or the page is over the publish cap.
entitlement_gateA dataset declares an entitlement block that cannot be published as written. See Row-level security.
seedA dataset's SQL did not run, or did not return the whole result.
conflictThe stored spec moved under you. Re-read and re-apply.
writeThe bytes or the row could not be stored.
internalA bug on our side. Nothing was published; please report it.

At most ten semantic findings are reported per publish, errors first. When more exist, a findings_truncated entry says how many of which severity were cut. Fix what is listed and re-run to see the rest.

Semantic checks

These run against the seeded data, not just the spec, which is why they can catch a declaration the data contradicts. An error blocks the publish; a warning does not.

CodeSeverityWhat it caught
col_missingerrorA declared dimension or measure has no matching output column.
col_extraerrorAn undeclared output column would ship as public island bytes. Declare it or drop it from the query.
type_not_numericerrorA measure needs a numeric column and its output column is not one.
agg_mismatcherrorThe measure declares one aggregate and the query computes another.
count_not_integererrorA count measure seeded non-integer values, so it is really an average.
predivided_ratioerrorA measure divides one aggregate by another in SQL. That re-sums wrong under viewer filters. Declare a ratio measure instead.
hybrid_rows_missing_columnerrorA hybrid measure's source column is absent from rows_sql, so the row-level path cannot recompute it.
lattice_no_grand_totalerrorThe lattice has no all-rolled-up cell, so the unfiltered dashboard boots blank.
seed_emptyerrorThe dataset seeded zero rows, so nothing it declares can be verified.
seed_truncatederrorThe island holds fewer rows than the dataset really has. The missing cells would refresh silently wrong.
seed_probe_unavailableerrorThe completeness probe returned no count, so there is no evidence the island is whole.
rate_shaped_sumwarningA sum over values all between 0 and 1. Summing rates is usually wrong.
percent_points_suspectwarningA measure declared percent/fraction seeded values that look like 0 to 100.
date_dim_not_isowarningA date dimension seeded non-ISO values. Bucket to YYYY, YYYY-MM or YYYY-MM-DD in SQL.
domain_drift_at_publishwarningA seeded value falls outside its declared domains, so the runtime filter will drop it.
series_cardinalitywarningMore distinct series values than the five-colour mark palette.
slice_cardinalitywarningMore distinct slices than a pie or donut will draw.
funnel_stage_absentwarningA declared funnel stage matches no seeded value, so it renders as absent.
stack_percent_mixed_signwarningA stack: percent column seeds both positive and negative segments, so its shares would be no true proportion.
declared_grain_over_cell_boundwarningA grain your page asks a served dataset for can exceed the query service's cell bound, so that grain would be refused. Each grain is priced on its own, because the service answers each as its own question and refuses each on its own, so the other grains of the same dataset keep answering. The message names which dashies.data call asks for it, the grain, the estimate, the bound and the widest dimension, once per grain that is over; ask for a narrower grain with by in that call, bound the dimensions with domains or buckets, or declare fewer. Estimated from the declared domains, the seed and your page's own subscriptions, on a dry run as on a publish. A dataset no call asks for is not estimated.
manual_cross_checkadvisoryA dataset is built over more than one row source.

`manual_cross_check` is the one no static check can close

A join that matches more than one row per key silently multiplies every additive measure, and nothing in the pipeline can see it. Two things make it worse than it sounds:

On a lattice or hybrid, each cell is still the exact aggregate of the duplicated rows, so every subtotal reconciles with every total and the dashboard looks internally consistent. Checking it against itself finds nothing. Measured at roughly 150 times inflation on a real star schema.

A rows dataset does not escape it by deferring the aggregation: the duplicated rows are already baked into the published island, and the query engine then aggregates them correctly and returns the inflated number.

The only thing that finds it is a comparison against an independent direct aggregate: Verify your numbers.

Named publish refusals

CodeWhat happened
over_publish_capThe compiled body is over the publish limit. The message names the byte count and the heaviest dataset.
spec_conflictThe stored spec changed since you read it. Re-read with get_dashboard_spec and re-apply.
spec_base_staleYour base_spec_hash no longer names the stored spec: it was republished after you read it.
spec_ref_unknownA spec_hash names neither the stored spec nor a document still held server-side. Send the document.
spec_too_largeThe result of applying spec_edits is over the spec byte limit.
slug_mismatchThe spec's slug differs from the publish path. The path is the target; rename with update_dashboard.
look_from_no_bodylook: { from } points at a dashboard with no published body to inherit.
look_from_mismatchlook: { from } must equal the publish target slug. To reuse another dashboard's body, read it and inline it.
custom_html_scriptA <script> in a custom tile's html is inert. Move runtime code to the tile's js.
null_leading_dimensionA dimension's leading value is null.
timezone_bucketThe SQL buckets time without naming a zone, or with an ambiguous single AT TIME ZONE. Not raised when the warehouse reports every value a bucket reads as a DATE taken from a DATE column.
parquet_pendingNot a failure: a dataset whose rows are kept outside the page publishes with no data until a refresh reads them. On a page of managed tiles those tiles read "Updating"; on a page whose markup you wrote, your own dashies.data callback is handed status: "pending" for that wait, so draw a "no data yet" state for it. The human report says this once per dataset, in one note that also stands for parquet_pending_manifest and, on a page whose markup you wrote, author_renderer_served_dataset, and it drops the wait from that note when the publish's First data: line says the rows are already served.
entitlement_requires_enterpriseA dataset declares an entitlement block, and row-level security is an Enterprise capability. One code, two sentences: the usual one says this workspace is not on the Enterprise plan and points at the plan page; the other says Dashies could not read this space's plan, so the publish was refused rather than admitted. On that one, publish again. check_readiness answers whether it is available in a space before you write the block.
entitlement_requires_served_modeA dataset declares an entitlement block and is not on the served path. The filter is applied where the data is queried rather than in the page, so the dataset has to declare mode: resolved.
entitlement_not_supported_on_engineA dataset declares an entitlement block and this dashboard's data source cannot have a per-viewer filter enforced on it yet. One code, two sentences: the message either names the data source and the ones that do work, or says Dashies could not establish which data source the connection is. Upgrading a plan does not change the first case.
entitlement_root_without_datasetA dashboard-level entitlement block with no dataset-level one. The dashboard-level block carries only the admin opt-out, so on its own it opts out of a filter that does not exist. Declare the entitlement on the dataset whose rows it should filter, or remove the dashboard-level block.
entitlement_removal_undeclaredThe dashboard you are publishing over filters its rows per viewer today, and this document declares no entitlement block, so publishing it would serve every row to every viewer. To remove the filter on purpose, set row_level_security_removed: true at the top level of the document; to keep it, restore the dataset's entitlement block. One code, two sentences: the other says Dashies could not read whether the dashboard filters, so the publish was refused rather than admitted. On that one, publish again rather than declaring the removal, which would unfilter a dashboard nobody has established filters.
entitlement_removal_contradictedThe document declares an entitlement block AND row_level_security_removed: true, which cannot both be true of one dashboard. Remove the removal declaration to keep filtering, or remove the entitlement block to stop.
expr_not_plain_expressionA measure declares a SQL expression that is not a plain expression: it carries a subquery, a relation reference, a table function or a semicolon. The expression is evaluated over a scratch relation holding only the viewer's own rows, so a subquery, a relation reference or a table function is refused here rather than failing later. The message names the measure and the word. If the word is a column name, quote it. extract, substring and trim are the only functions whose from separator is accepted; date_part, year, substr and the two-argument trim spell the same thing without one. The check is per DASHBOARD, not per dataset: a dashboard where any dataset declares an entitlement block has every expression measure checked, and a dashboard that declares no entitlement block anywhere is not checked at all.
compiler_invariant, internal_errorA bug on our side. Nothing was published; please report it.

Refresh failure classes

A scheduled refresh that fails records one class on the run. The last good body stays live, so a reader never sees a half-refreshed dashboard.

ClassRecorded messageWhat to do
connection_authwarehouse connection authentication failedThe credential is wrong or revoked. Rotate it in the web app.
connection_unreachablewarehouse was unreachableNetwork, firewall, or a paused warehouse.
connection_timeoutwarehouse query timed outThe cube is too slow for the refresh budget. Narrow it.
ssl_failedwarehouse TLS/SSL negotiation failedA TLS problem at the warehouse.
schema_driftwarehouse schema changed; cube query no longer matchesA table or column was renamed or dropped, the query asks for a name it does not return, or the login can no longer read the object, which Snowflake reports the same way. Fix the SQL, or restore the login's access. On a PostgreSQL data source not added as only accepting known IP addresses, press Resync schema first, so the schema your AI tool reads from Dashies is current.
cube_errorwarehouse cube query failedEverything else the query raised.
tier_lapsedthe plan that pays for this dashboard's refresh has lapsed; paused until it is upgradedNot a failure of the query. It resumes on upgrade.
connection_removeddata connection is no longer activeThe connection was deleted or disabled. Dependent dashboards were paused.
extractor_hostthe Dashies extraction service failed; this is ours, not your warehouseThe refresh failed inside Dashies, not at your warehouse. Nothing in the connection or the cube needs changing. Read the recorded reason for which part failed.
audit_refusedthe data this refresh produced did not pass its checks against the previous version, so the dashboard is still serving the last version that didNot a fault at either end. Nothing was published and the previous numbers stay live. Read the recorded reason for which check refused.

Only three of those are evidence the connection itself is unhealthy: connection_auth, ssl_failed and connection_unreachable. The rest are about one dashboard's query, which is why a connection can read active while one of its dashboards keeps failing.

`connection_timeout` is deliberately not a connection-health signal

A timeout means a slow cube on a healthy warehouse. Counting it would flip a connection to failing because one dashboard grew too big, and take every other dashboard on that connection with it.

Never the raw driver text: the executor drops the message and the detail before the class is recorded, because they carry the remote host, user, database and port.

Authentication failures

A request to the web app carrying an unusable session cookie returns 401 {"ok":false,"error":"unauthenticated (<reason>)"}. The reason is a stable machine token, mainly for log analysis:

ReasonMeaning
no_cookieNo session cookie was sent.
malformedThe cookie or its token could not be parsed.
expiredThe token is past its expiry.
invalid_signatureThe signature did not verify.
wrong_issuerThe token was issued by something else.
wrong_audienceThe audience claim does not match: for example an anon key used as a session.
no_subThe token carries no subject.
fetch_failedThe signing keys could not be fetched. Transient.

Any of these means sign in again. An MCP tool never surfaces these: the client re-runs the browser flow on its own. See the authorization flow.

Check it worked

Publish with dry_run: true first. It runs the whole pipeline, seed included, and writes nothing, so every finding above is reachable without creating a dashboard. A clean dry run and a failing real publish differ only in the write layer.