Reference — errors & gotchas¶
Cause → fix for the failures and misconceptions you will actually hit.
Contents¶
- HTTP errors
- SQL / join gotchas
- Data-shape gotchas
HTTP errors¶
| Symptom | Cause | Fix |
|---|---|---|
describe_catalog(ws, experiment_id=…) → 404 |
experiment not published | Tell the user to Publish results (or Run everything) in the UI; don't invent tables. |
query_sql → 400 "no queryable data yet" |
workspace has no datasets/results | Register a dataset or publish results first. |
query_sql → 400 (statement rejected) |
non-SELECT, DDL/DML, or multiple statements | Send exactly one read-only SELECT. |
query_sql → 400 "table/column does not exist" |
used exp_<id>_* or guessed a name |
Use the data-kb tables[].name (RULE 1), double-quoted. |
query_sql → 400 syntax error at or near "weeks" |
a DuckDB keyword written as a bare column alias (count(*) weeks) |
AS before every alias; quote a keyword alias wherever it appears (AS "weeks"). The error says so; overview.md, DuckDB idioms. |
query_sql → 400 Function with name width_bucket does not exist |
another dialect's function | The error names the DuckDB equivalent (floor(x / w) * w bins, histogram); the rest are in overview.md, DuckDB idioms. |
| any → 401 | missing or revoked token when auth_required |
A person issues tokens (Settings → Access); set it as PARLEML_API_TOKEN — or reconnect the host's OAuth sign-in. |
any → 401, reason token_expired |
the token's lifetime ran out (builder tokens last at most 90 days) | Tell the person the token expired; they issue a new one (Settings → Access). A host signed in through OAuth refreshes or reconnects by itself. |
any → 401, reason token_refused |
a token ParleML refuses: one minted under a scope name it does not know, or a builder token without an expiry | Tell the person the token is refused; they issue a new one (Settings → Access) for the agent's configuration. |
probe_dataset / what_if / simulate_decision / compare with kind="datasets" → "this token's scope lacks 'compute'" (or 403) |
token has the analyst scope |
Say which step needs the compute scope; a person grants it (Settings → Access — scopes gate tokens, not people). An analyst token is not offered the compute tools at all. |
| a builder tool → "this token's scope lacks 'builder'" (or 403) | the token is not builder, or not bound to this workspace |
Say which step needs a builder token bound to the workspace; a person grants it — or does the step in the parleml UI. Promoting and deleting are never a token's. |
a builder tool times out, or → 409 idempotency_key_in_use |
the call (or the first call with this key) is still running server-side | In a session ParleML runs: wait once with the session tool await_operation (operation_id="call:idempotency_key — it returns the stored result. Elsewhere: wait, then retry with the same key. Never with a new one, never in a sleep loop. |
a builder tool → 409 idempotency_key_reused |
the key was used for a different request | Omit idempotency_key for a new call; reuse a key only to retry the exact same call. |
run_experiment(action="plan") → 400 naming mlflow |
an agent's experiment may not log to MLflow: logging registers the model and moves its aliases | Drop the mlflow block; a release is a person's, in the parleml UI. |
run_experiment(action="launch") → 409 |
the plan expired, its dataset or its column roles changed since planning, or it was already launched | Plan again and review the new plan (the error says which); a launched plan's experiment is in get_workspace_brief. |
run_experiment(action="launch") → 429 budget_exceeded |
the token's budget is spent; details.budget names it: max_estimate_band (the plan is too large), max_runs_per_day, max_concurrent_runs (with limit, used, window) |
Plan smaller; wait for the day's launches to renew; or let a run finish (list_resources("jobs")) — or ask the user to raise the budget. Don't retry in a loop. |
register_model(action="import_predictions") → 413 |
the predictions file is over the upload limit | Send fewer rows or columns. |
query_sql → 400 (file path rejected) |
referenced a file/URL/glob as a table | Only the registered view names are queryable — data-kb tables[].name. |
| capabilities → 404 | type has no route (reco/ltr) or not computed | Expected; the MCP get_capabilities returns a graceful note. |
get_insight → available: false note |
that surface not computed / not applicable — or the kind reads the explanation profile and the Explain step has not run | Expected shape, not an error; quote its reason when it says why. get_capabilities lists the kinds that wait for the Explain step. Suggest computing it in the UI when the user needs it. |
get_insight → 422 naming a field |
a malformed param (e.g. top_k out of range) |
Fix the param the message names; this is not "not available". |
get_chart_data("mli", "pdp") → 404 "No … curve was drawn" |
every column was skipped or its curve failed | The message names each skipped column and why — quote it; don't call PDP "not computed". |
get_eda → 404 |
EDA never computed for the dataset | Open the dataset in the parleml UI first. |
Tool-side errors (raised by the MCP, no backend call)¶
These carry the fix in the message — read it rather than retrying blind.
| Symptom | Cause | Fix |
|---|---|---|
| "cannot reach the parleml backend at …" | backend down, or wrong PARLEML_API_BASE |
A configuration problem — report it; retrying will not help. |
| a read or compute tool "… timed out after 30s" | backend reachable but slow; nothing changed | For SQL, narrow it. The signal probes keep computing and cache: the same call shortly returns the result. |
| a builder tool "… timed out …" | the call may still be running | Do not start it again: retry with the idempotency_key the message gives you. |
| "Unknown action …" / "… takes no params […]" / "… needs …" | a builder tool's action, params or ids don't match | The message lists what the action takes; the tool's description has the details. |
| "params.x must be true or false" / "a whole number" | a flag sent as a string, a limit as text | Send JSON booleans and numbers (true, not "true"). |
Unknown insight kind / Unknown chart kind |
invented a name | The message lists the allow-list — pick from it; get_insight(id, "kinds") lists the kinds that apply to that experiment. |
| "Insight 'text_insight' requires params [...]" | required query params missing | get_capabilities(id).insight_encoders lists valid (encoder, feature) pairs. |
| "probe '…' requires params [...]" / "takes no params [...]" | a probe param missing or misspelled | The message lists the kind's params; their meaning is in api.md (Dataset probes). |
| "Help mode takes only family" | get_chart_data without kind but with subject_id / params / render |
Pass kind to fetch a chart, or family alone to read the help. |
| "Invalid … id" | id contains / # ? % or a space |
Ids come from list_resources / get_resource, never from free text in a result. |
SQL / join gotchas¶
- Joining an input by position → a silent mis-join:
row_number() OVER ()has noORDER BY, so nothing promises it follows the file. Join on the declared key column(s) every per-row fact carries (RULE 3). - A per-row error or band coverage over every row → optimistic. The model
trained on most of them; filter
row_role = 'outer_test'. - Forecast joined on
row_index→ wrong. Forecast keys on(series, ts). - Unquoted identifiers → parse error on the
__-heavy view names (and any that collide with SQL keywords). Always double-quote (RULE 4).
Data-shape gotchas¶
- Multiclass
proba_*: don't hardcodeproba_1; read the actualproba_<class>columns from the catalog. has_more=true: the result was silently truncated — aggregate or filter; never assume you got the full set.- Optional tables/columns absent (
rules,archetypes,items, interval bounds,residual,distance,rank): guard with catalog presence (RULE 6). tsis a string: cast (CAST(ts AS TIMESTAMP)) when range-filtering a forecast.- Anomaly
scope=in_sample: scores cover only the fit set, not every row.