Troubleshooting

What the common errors mean and how to fix them, from a deploy the loader rejects to a query the planner refuses. 0sql refuses a query it cannot answer correctly instead of returning a wrong number; the message names the field or combination it could not resolve, and that is the thing to fix in the model, not something to work around in the request.

Every HTTP error has the same shape, and zsql prints it as <class>: <message>:

{"error": {"class": "Planner::ResolutionError", "message": "No universe can resolve the query within datasource tpcds."}}

Quick reference

SymptomStatus and classStart here
no project.yml here or abovelocal zsqlDeploy errors
Error in models/...: ...400 DeployErrorModel validation
the archive holds no project.yml400 DeployErrorDeploy errors
tests failedexit code only; deploy was 200Deploy errors
this branch has security policies; a context is required to plan400 ContextRequiredPlan errors
No field named 'x' in this model.422 Semantic::NotFoundFuzzy field corrections
No universe can resolve the query within datasource ...422 Planner::ResolutionErrorQuery resolution
Security policy context dimension '...' is not reachable422 Planner::SecurityPolicyErrorQuery resolution
an API key is required / this API key is not valid401 UnauthorizedAuth errors
query key ... is not granted ...403 ForbiddenAuth errors
A field you did not ask for appears in the SQLcorrections in the responseFuzzy field corrections
Numbers too highmodel, not requestNumbers look wrong

Deploy errors

zsql deploy tars the project directory and POSTs it; zsql check does the same against /validate and stores nothing. Both fail with a 400 DeployError whose message says which stage refused.

no project.yml here or above; run zsql init to start a project

Local. zsql walks up from the current directory looking for project.yml. Run it inside the project, or pass --project <dir>.

the body must be a tar.gz of the project directory, unpacking the archive: ..., the archive holds no project.yml

Only reachable when you POST to /deploy yourself. The body must be a gzip tar with project.yml at its root or inside one top-level folder. zsql deploy builds this for you.

Error in <file>: <message>

The loader rejected a file. The message is one of the model validation messages below; fix the file and run zsql check until it is clean, then zsql deploy. Nothing is deployed when any file fails: the branch keeps its previous model.

snapshot has N integrity problems:

A compiled model that references something missing. Each line is of the form expression <e> references missing field, join <j> left table <id> is missing, field <f> mapped twice on table <t>, table <t> snapshot date <f> is not a dimension, partition <p> dimension <d> is missing or not a dimension, or duplicate <kind> uid <uid>. Almost always a rename: a table or field was renamed in one file and still referenced by its old name in a relation, a snapshot:, a partition or a formula.

query keys are read only; deploy with your personal key

  1. The key in .zsql or ZSQL_API_KEY starts with zqk_. Deploy with a zsk_ key. See Auth errors.

Deploy succeeded but zsql deploy exited with tests failed

The server returns 200 even when tests fail; the model is live. zsql prints PASSED, FAILED or ERROR per test with --- generated and --- expected blocks, then exits non-zero. A failing assert_sql usually means the model changed and the expected SQL is stale: compare, update the test, redeploy. ERROR means the test’s projections no longer plan (a renamed field) or its assert_regex does not compile. See Tests.

Warnings

A deploy can succeed with warning: lines. They are worth fixing:

WarningMeaning
<file>: table <name> has no cost; 0 assumedAdd cost: so routing prefers the right table.
<file>: table|field|join <k>: unknown key '<k>' is ignoredA typo in a YAML key. The setting is not applied.
<file>: partition on <dim>: predicate <p> is not supported / needs a number or date dimensionThe partition will not rank the table. See Partitions.
formation: root <r> reaches <t> by two routes of cost <c>: ...Two equal-cost join paths; one was chosen. Set costs so the choice is deliberate.
formation: ambiguous: from <r> the field "<f>" is reachable at cost <c> through A and B; A is usedSame field reachable through two dimension tables. Set costs or split the dimension.
policy <uid> context dimension <d> not found; policy is inertThe policy’s context_dimension was removed or renamed. The policy no longer protects anything.
project.yml not found; the directory name stands in for the projectAdd a project.yml with a stable uid.

Model validation

Loader messages arrive as Error in <file>: followed by one of these. The ones people hit most:

MessageFix
datasources.yml is required / datasources.yml defines no datasourceAdd at least one datasource keyed by a short id.
Datasource errors: adapter '<a>' is not supportedUse one of the supported adapters. See Datasources.
Datasource errors: tier '<t>' should be hot, warm or cold (<name>)Fix tier:.
datasource key is required in table file.Every tbl.*.yml and rel.*.yml names its datasource:.
datasource with uid or name <v> not found in branch.The datasource: value does not match a key in datasources.yml.
Table errors: Name has already been taken (<name>)Two table files share a name.
Table errors: Physical name can't be blank (<name>)Add physical_name:.
fields must be a list (<name>) / each field of <name> must be a mappingYAML shape. Indentation (spaces, not tabs), a missing - or colon.
Field <name> errors: type should either be a dimension or measuretype: is required on every field.
Field <name> errors: Data type '<d>' is not included in the listSee Data Types.
Field '<name>' is missing an expression node. / Expression errors for <name>: Sql can't be blankEvery field needs expression: { sql: ... }.
Expression errors for <name>: Sql measure should have an aggregation functionA measure’s sql must aggregate: sum(ss_net_paid), not ss_net_paid.
Expression errors for <name>: Column refs dimensions should reference a table columnA dimension’s expression must read a column of its table.
Expression errors for <name>: Field <name> is already mapped to this table.The same field twice in one table. The same name on another table is not an error: it is one concept.
Field <name> errors: Grains contains invalid values: <bad> / Snapshot '<x>' is not included in the listThe message lists the accepted values. See Snapshot measures.
Field <name> errors: Exclusion rule Type should be one of: dimension, table, universeSee Exclusions.
JoinDef errors for <key>: Cardinality can't be blank / Cardinality '<x>' is not included in the listmany_to_one, one_to_many or one_to_one. Many-to-many is not supported; model a junction table with two relations. See Cardinality.
JoinDef errors for <key>: Sql join sql should be of the format left.column = right.columnOnly equi-joins, written with the left. and right. prefixes.
Left table '<x>' not found in datasource <ds> / Right table ...Relations never cross datasources, and names must match the table files’ name.
Join: <<left>-<right>>, already exists.Two relations between the same pair of tables.
Circular import detected: a.yml -> b.yml -> a.yml / Import not found: <import>See Imports.
could not find snapshot dimension <dim> in branch.The table’s snapshot: must name a date dimension.
Partition errors: Dimension must exist (<dim>) / Predicate is not included in the list (<dim>)See Partitions.
[<field>]: could not find measure named: <m> / could not find dimension named: <d>A [Name]@m or [Name]@d reference in a formula does not exist. Names are matched case-insensitively; check spelling.
[<field>] on <table>: Sql Referenced dimensions in the formula could not be located in a single Universe.A compound measure references a dimension not every component can reach. Move the conditional part into a standard measure on the fact that has the dimension. See Compound Measures.
[<field>] on <table>: Sql Could not find a datasource which had all of the required measures. Cross datasource queries are not supported.Components of a compound measure live in different datasources. Define the missing measure in one of them under the same name.
[<field>]: Inclusions Dimensions [...] could not be found in a universe with this measure.See Inclusions.
Policy '<n>': context_dimension '<d>' not found in this branch. / trigger field '<f>' not found in this branch.Names in security.yml must match a field in the branch. See Security.
Unknown mode '<x>'. Use mask_data or filter_data. / Policy '<n>': unresolved '<x>' should be deny or allowFix the value.
Test must have a name / Test must have at least one field in 'projections' / Test must have at least one assertion (assert_sql or assert_regex)See Tests.

Plan errors

POST .../sql, /explain and /explore answer 400 for a malformed request, 404 when the branch is not deployed, and 422 when the request is well formed but the model cannot answer it.

StatusClassMessageWhat to do
400Invalidgive a spec or an exprThe body needs spec or expr.
400Shorthandthe parse errorThe expr line did not parse. See Shorthand.
400ContextRequiredthis branch has security policies; a context is required to planSend a context. See Security.
404NotFoundno deployment for project <uid> branch <branch>Deploy that branch, or check --branch (the default is the checked-out git branch). zsql list shows what is deployed.
422Semantic::NotFoundNo field named '<x>' in this model. optionally followed by Did you mean: A, B, C?No field matches by name, uid or synonym. zsql fields <x> searches. See Fuzzy field corrections.
422Semantic::AmbiguousField '<x>' is ambiguous ...A dimension and a measure share the name. Write <x>@d or <x>@m.
422Query::Spec::InvalidSpecErrormalformed spec: ...Unknown keys, a bad predicate name, a bad decorator type or a segment that is not well formed. See The query spec.
422ActiveRecord::RecordInvalidValidation failed: ...A calculation or filter failed validation: a raw column in a formula, an unresolved [Name]@m reference, a blank filter value.
422Planner::ResolutionErrorAt least one projection requiredProject at least one field.
422Planner::ResolutionErrorNo universe can resolve the query within datasource <ds>.The fields cannot be joined. See Query resolution.
422Planner::ResolutionErrorCould not find path for <x> / Could not find Universe for measure|dimension: ...Same cause, named more precisely.
422Planner::ResolutionErrorcannot parse date ...Use YYYY-MM-DD or a relative date such as 28d, 3m, 1y. See Filters.
422Planner::ResolutionErrorUnsupported filter predicate: ...The predicate does not apply to that field’s data type.
422Planner::ResolutionErrorCalculation <x> references itself through <y>Break the cycle between calculations.
422Planner::ResolutionErrorCould not resolve segment <s>: ...The segment’s keys are not reachable from the measures it constrains. See Segments.
422Planner::ResolutionError<table> is not a snapshot tableA snapshot measure on a table without snapshot:.
422Planner::SecurityPolicyErrorSecurity policy context dimension '<d>' is not reachable in the universe for this query. Cannot safely enforce security policy.See Query resolution.
422Planner::SecurityPolicyErrorSecurity policy context dimension '<d>' has a composite expression and cannot be used as a security context dimension.Point the policy at a plain column dimension.
422Unimplementednot implemented: ...The combination is not supported yet (a custom predicate, a calculation over a rule-bearing measure).

zsql explain is the fastest way to see why: it prints the node tree, which table each node reads and the paths it joined. See Explain and the full list in Query errors.

Query resolution

No universe can resolve the query within datasource <ds>.

The request asked for a measure grouped or filtered by a dimension its fact cannot reach, and no blend between facts covers it. 0sql refuses rather than guess a join. Check, in order:

  1. Is there a relation? The dimension’s table must be reachable from the measure’s fact through many_to_one (or one_to_one) joins. Add the missing rel.*.yml.
  2. Is the change deployed? zsql deploy, then zsql tables and zsql fields <name> show what the branch has.
  3. Is it the wrong concept? If the dimension exists under a different name on the fact’s side (Ship Country vs Country), it is not the same field. Unify the names if they are one concept, or ask for the right one.
  4. Should the measure ignore it? If the measure is meant to be grouped only by some dimensions, model that with an exclusion.

Remove fields one at a time to find the culprit, or ask zsql explore --expr "<the fields that work>" which dimensions and measures can still be added.

Every plan runs in one datasource

A query is answered from exactly one datasource; results are never merged across them. If the fields you need live in two, define the missing measure on a table in the other datasource under the same name, and routing picks whichever datasource can serve everything. See Semantic routing.

Security policy context dimension '<d>' is not reachable in the universe for this query.

A policy fired (the query projects a tagged or named field) but the node’s universe has no path to the policy’s context_dimension. The query is refused because the filter or mask could not be applied. Either give that table a join path to the context dimension, or pick a context dimension every table with a triggered field can reach. See Security.

Fuzzy field corrections

A field reference in a spec or an expr is matched by name, uid or synonym, case-insensitively. When nothing matches exactly, the planner scores every field of the wanted kind by trigram overlap with the reference and applies this rule:

  • A reference shorter than four characters never corrects; it resolves exactly or fails.
  • If the best candidate scores at least 0.5 and leads the runner-up by at least 0.2, it is used silently and reported in the response’s corrections array.
  • Otherwise the request fails with Semantic::NotFound, with up to three candidates scoring at least 0.25 appended as Did you mean: A, B, C?.
{"sql": "...",
 "corrections": [{"term": "departmnt", "field_uid": "department", "field_name": "Department", "score": 0.8}]}

corrections is omitted when empty. zsql sql prints each one to stderr as departmnt → Department (~0.8). If a correction surprises you, use the exact name or the uid, and consider adding the misspelling as a synonym on the field so it resolves exactly.

Auth errors

StatusClassMessageFix
401Unauthorizedan API key is required: Authorization: Bearer <key>Send the header. zsql reads the key from ZSQL_API_KEY, then .zsql, then ~/.zsql/config; run zsql auth --api-key ... in the project.
401Unauthorizedthis API key is not validRevoked, rotated or mistyped. Make a new one in the console.
403Forbiddenquery keys are read only; deploy with your personal keyUse a zsk_ key for deploy, check, test and remove.
403Forbiddenquery key <name> is not granted <uid>/<branch>An owner grants the key that project, with branch as a name, *, or omitted for the production branch.
403Forbidden<email> has <level> access to <uid>; this needs <level>Ask an owner to raise your level. Read: query. Write: deploy. Owner: access and settings.
403Forbidden<email> has no access to <uid>The project is restricted; an owner adds you.
403Forbiddendeploying to the protected production branch '<b>' of <uid> needs an ownerDeploy a feature branch, or have an owner deploy.

See Accounts, keys and access.

Numbers look wrong

0sql returns SQL, so a wrong number is a wrong model or a wrong request. zsql explain shows which table and joins produced it.

Numbers are too high (double counting)

  • Cardinality is wrong. A relation declared many_to_one that is really one-to-many multiplies the measure. Check the data and fix cardinality. See Cardinality.
  • allow_measure_expansion: true on a join that is not safe. Expansion lets a measure be grouped by dimensions on the many side. Remove it unless the join really is safe.
  • Many-to-many modelled as a direct join. Use a junction table with two relations.

The same measure gives different numbers in two requests

When a measure is defined on more than one table, routing chooses per request by the requested dimensions, then by cost. If two definitions of Store Net Paid disagree, they are not the same concept or one has a bug. Run zsql explain on both requests to see which table served each; give a distinct name to anything that means something different.

A balance or inventory total is wrong across months

Summing a daily balance over a month is meaningless. Model it as a snapshot measure with snapshot: ending (or beginning) on a table that declares its snapshot: date dimension.

A cross-domain compound measure repeats a value across rows

Expected. When a request groups by a dimension only some components of a compound measure can reach, the others are auto-levelled: aggregated without that dimension and repeated across it. For a rate this is the fixed denominator you want. See Compound measures.

Getting help

  1. zsql check for the full list of loader errors and warnings.
  2. zsql explain --json --expr "..." for the plan of a refused or surprising request.
  3. Narrow it down: git diff HEAD~5 -- models/ shows what changed; drop fields until the request plans.
  4. Send the Error in lines, the explain JSON, the relevant YAML and what you expected.

Next steps