Skip to main content

Errors

Every error response has the same shape:

{
"ok": false,
"error": {
"code": "E_UNKNOWN_MATERIAL",
"message": "Material 'allpvs' not found in manifest.",
"at": "materials[0].name"
}
}
  • code — a stable, machine-readable error code. Clients should branch on this, not on message.
  • message — a human-readable explanation. The wording can change between releases.
  • at — a JSON-pointer-ish path into the request body showing where the offending value lives. Optional; present when the error is tied to a specific field.

Canonical error codes

Authentication / authorization

HTTPCodeWhen
401E_UNAUTHORIZEDMissing or invalid Authorization header.
403E_FORBIDDENAuth succeeded but the user cannot access this tracking_id.

Request structure

HTTPCodeWhen
400E_INVALID_JSONBody did not parse as JSON.
400E_MISSING_FIELDA required top-level field is absent.
400E_UNKNOWN_FIELDAn unrecognized top-level key appeared.
400E_TIME_REQUIREDtime.start, time.end, or time.tz is missing.
400E_INVALID_TZtime.tz is not a valid IANA timezone.
400E_INVALID_TIME_RANGEtime.end <= time.start, or the range is implausible.

Materials, views, result

HTTPCodeWhen
400E_UNKNOWN_MATERIALA material name is not in the manifest.
400E_UNKNOWN_VIEWresult.use does not reference a view defined in make.
400E_UNKNOWN_COLUMNA keep, filter, or sort entry references a column not in the target material.
400E_DUPLICATE_VIEWTwo views in make share a name.
400E_LIMIT_REQUIREDresult.limit is missing and count_only is not set.

Features

HTTPCodeWhen
400E_FEATURE_DISABLEDThe request uses a feature whose enabled is false on this server.
400E_FEATURE_NOT_AVAILABLEThe feature exists but the server's api_update is older than the feature's since.

Calc validation (2026-05-11+)

HTTPCodeWhen
400E_CALC_COLUMN_UNRESOLVEDA material.column reference inside calc cannot be resolved against the view's scope: either the material is not in from or join.with, or the column does not exist in that material's schema. Since: 2026-05-11. View-chain views (where from[0] is a previously defined view) are intentionally excluded from this check and fall back to the Material-runtime path.

E_CALC_COLUMN_UNRESOLVED replaces what used to be a silent zero-row result under the predecessor 2025-10-20 version. It is intentionally loud so AI clients receive a clean repair signal instead of accepting a wrong-but-successful response.

Example:

{
"ok": false,
"error": {
"code": "E_CALC_COLUMN_UNRESOLVED",
"message": "calc references 'click_event.pv_id', but 'click_event' is not in this view's from or join.with scope.",
"at": "make.top_clicks.calc.clicks"
}
}

JOIN validation

HTTPCodeWhen
400E_INVALID_JOINjoin structure is malformed, not fully qualified, non-equi, or on.left / on.right does not resolve to the correct side of the join. Since 2026-05-11 (originally 2026-05-10 on 2025-10-20) error responses include structured details to identify which side failed.
400E_BAD_JOINJOIN attempted on a column that is not a declared join key.
400E_CARDINALITY_GUARDA JOIN would explode row count beyond the safety threshold (e.g. unfiltered gscallpv).

E_INVALID_JOIN details fields (since 2026-05-11):

FieldTypeMeaning
sidestring"left" if the offending value is in on.left, "right" if in on.right.
received_valuestringThe literal value the request passed (e.g. "click_event.pv_id" or "top_pages.pv_id").
expected_prefixstringThe prefix the validator expected on this side. For on.left this is the resolved physical material name of the from side (with view chains pre-resolved). For on.right it is the join.with string verbatim.
hintstringA human-readable repair suggestion (e.g. "use 'allpv.pv_id' — view-name prefixes are not accepted on on.left").

on.left must equal the resolved physical material name of the from side, even when from[0] is a view chain — the validator resolves the view back to its source material name before comparison. Bare view-name prefixes on on.left are rejected. on.right must equal the join.with string verbatim.

Example:

{
"ok": false,
"error": {
"code": "E_INVALID_JOIN",
"message": "join.on.left must reference the from material, not a view name",
"at": "make.top_clicks.join.on[0].left",
"details": {
"side": "left",
"received_value": "top_pages.pv_id",
"expected_prefix": "allpv",
"hint": "Replace 'top_pages.pv_id' with 'allpv.pv_id'. on.left always uses the physical material name; the view chain is resolved by the validator before comparison."
}
}
}

Execution

HTTPCodeWhen
500E_EXECUTOR_FAILEDInternal execution error. Retry with backoff.
504E_TIMEOUTQuery exceeded the server's time budget. Narrow time, add filters, or drop expensive JOINs.

Retry strategy

  • 401/403 — do not retry. Fix credentials.
  • 400 (structural) — do not retry blindly. The client has a bug. If an AI client gets a 400, it should re-read /guide in case the schema moved, then rethink the query.
  • 500 — retry once, with jitter.
  • 504 — do not retry the same query. Narrow the request.

Where to go next

  • /query reference — the request shape error codes refer to.
  • Examples — working requests you can use as a baseline when debugging.