Skip to content

15 · Errors, refusals and troubleshooting

A request can fail in a handful of ways that each call for a different response: a corrected field, a key, a different pair of choices, a smaller file, a wait. This chapter is every error the service returns, every refusal with the reason it exists, the exceptions the Python client raises for them, and the integration failures teams meet most, the commonest first.

Errors

Every error is a JSON body with a code and a message, and the HTTP status says how to respond to it: 400 wants a corrected field, 401 a key, 403 a grant your plan holds, 409 a different combination of choices, 410 a new solve, 413 a smaller file or a second request, 429 a wait, 503 the same request again shortly. Generated from the service's own error classes:

Status Code When
400 invalid_request Schema violation: unknown mode, unknown config key, operand from another mode.
400 signup_link_invalid An expired, used or unknown link.
401 unauthenticated No key, a malformed header, or a key that does not verify.
403 google_signin_refused An ID token that fails a check, or an unverified Google address. Names the check.
403 not_entitled The key is valid and does not carry the grant this request needs.
403 not_in_tier The key's tenant is on a tier that does not include this.
403 package_not_entitled The package feed, asked by a credential without the packages grant.
403 provider_host_not_allowed A caller-named model endpoint that the worker may not call.
403 test_key_restricted A qls_test_ key asked for something only a live key may have.
409 contract_error A billing request this tenant's state does not allow: billing_not_available, subscription_exists or brl_price_not_eligible. HTTP 409, the code is the slug.
409 idempotency_key_reused The same idempotency key, submitted with a different request.
409 refused A well-formed request whose combination of choices cannot answer the question.
409 signup_address_refused A disposable or undeliverable address.
410 artifact_purged The artefact existed and its bytes no longer do.
413 input_too_large The caller sent, or declared, more bytes than their cap allows.
413 payload_too_large The caller asked for more in one response than one response may carry.
422 generation_unusable The model answered, and what it answered cannot be used.
429 quota_exhausted A limit on the key is spent, or a concurrency cap is full.
429 signup_rate_limited Sign-up or sign-in past a per-address, per-IP or per-domain limit.
502 provider_error The customer's model provider refused or failed a call made on their behalf.
503 billing_unavailable A billing route on a host with no Stripe keys.
503 queue_saturated A queue class holds more pending work than its ceiling.
503 service_unavailable A store this request cannot run without is unreachable right now.

A 429 that clears by itself carries Retry-After. 429 quota_exhausted does not clear by itself: it names the meter, what was used, the cap, and when it resets (chapter 16).

Refusals

A refusal is a 409 refused with a refusal slug: a well-formed request whose combination of choices cannot answer the question it asks. Each is refused by name, because the alternative in every case is a run that answers a question nobody asked and looks exactly like one that did. A refusal before the job is queued costs nothing; one when the job runs is a job that finished with the refusal as its answer.

Refusal Refused Why
enumerate_needs_checkable_profile before the job is queued A count of the contexts that break a policy is only worth reporting when the run producing it is one an auditor can check. Neither profile offers a certificate, so the count would be unfalsifiable. Upgraded silently would be worse: the caller asked for a cheap run and would be billed for a proof.
enumerate_outside_guardrail before the job is queued There is one obligation to enumerate the contexts of, and only that mode names one.
batch_repair_needs_certificate before the job is queued A row owes a repair when it expects SAT or STALL and has not waived the seat belt -- SAT and STALL equally, because failing to rule an outcome out compels the same response as proving it reachable. Such a row is gated on the repair's invariants, so a run that cannot produce one cannot answer it. Left to run, the gate reports the absence of a repair nobody looked for: a row whose verdict matched exactly still fails, and the build goes red on a correct policy.
batch_certificate_required_unsatisfiable_config before the job is queued Such a row is discharged by a refutation a third party can CHECK, and a config setting certificate_detail none leaves nothing to check, as does one setting enable_phase2 false, which leaves no refutation derived.
sequence_needs_chain before the job is queued The cumulative question over one step is the single-turn question, and answering it silently would let a caller believe they asked something else.
pins_belong_in_the_table before the job is queued A property's pins belong in its row of the table, where they are reviewed with it. On the command line they would apply to every question at once, which is the over-pinning failure the table exists to make visible. An enumeration proposes pins rather than taking them, and the pin search is what decides which pins an obligation needs.
suggested_table_not_promoted before the job is queued This table still carries the placeholders an enumeration leaves behind. A candidate row is a proposal: the id names one question for as long as any record refers to it, and the expectation is the claim being checked, and neither is something derivable from the policy. Running the table with them blank would be promoting a proposal into a commitment by submitting it, which is the review step this whole layer exists to make cheap.
forest_profile_keys_are_sealed before the job is queued The forest profile's keys are what an answer on this mode means, and this mode will not run with them edited -- except oracle_unsat_admissible, which a request may turn off to accept only derived refutations. The endgames matter most: with Phase 3 and Phase 4 both off a stall means nothing at all. Choose the profile or choose the keys; on this mode they are the same choice.
root_bound_needs_closure before the job is queued max_roots > 1 requires enable_phase3; only the closure endgame enumerates a variety. A request for several roots without it cannot be honoured, and returning one anyway would misreport the result.
emission_needs_rails_config when the job runs An unphased answer cannot distinguish a check on the way in from a check on the way out; a whole-message property over a streaming flow is a proof about a message that never exists at run time.
bad_context_phase before the job is queued A phase the rails cannot name would register a wire the policy never mentions, and pin it to a value nothing reads. A property table's context takes the same form and is held to the same rule.
forest_region_needs_default when the job runs Fixed and free ask different questions -- one is about the neighbours of a record, the other about a whole population -- and a tool that picked one would be choosing the author's question for them.
forest_feature_needs_domain when the job runs An unbounded feature yields counterexamples outside anything the model will ever see: a negative balance, an age of four hundred. Those findings are true about the model and useless to its owner.
forest_domains_not_confirmed when the job runs Bounds inferred from a sample are facts about the sample. Whether a bill may be negative or an age is fixed is a claim about the world, and a person confirms it before a verdict rests on it.
forest_anchor_outside_domain when the job runs A region built round an impossible point asks about impossible neighbours, and a one-hot group with two members set, or none, is not a record.
forest_region_needs_anchor when the job runs A robustness property is robust around a record, and a fixed feature or a ball is fixed to or centred on one. Without the record the question has no subject.
forest_unsupported_construct when the job runs Equality and set-membership splits, preprocessing inside the graph, a non-monotone transform, or a Treelite key the pinned 4.x dump does not write: each is refused by name rather than approximated.
forest_model_mismatch when the job runs A table carrying a model_sha256 that is not this model's is refused, because a table reviewed against one model is not evidence about another.
obligation_not_on_that_rail when the job runs Refused rather than answered, naming the rails it is consulted on. A check placed at the wrong end of the pipeline is not the control it resembles, and answering the unqualified question instead would report it as one.
pin_names_the_obligation when the job runs The obligation is pinned to 0 to ask whether the action can fire without it, and a conditioning pin on the same variable is written afterwards into the same wire. The pin wins, so the run answers the opposite question under that row's id -- and the verdict, the certificate and the audit entry all stay internally consistent about the question they think was asked, so nothing downstream can catch it.
seatbelt_waiver_missing before the job is queued Waiving the seat belt is the one table edit that turns a red row green without touching the policy, so it must leave a sentence behind saying why: a sweep that cannot fail is not evidence. On an UNSAT row there is nothing to waive -- no repair is produced -- so the field would read as a constraint the run is honouring while it honours nothing, and would become load-bearing the day the row's expectation changed. max_sds beside a waiver is refused for the same reason.
ir_version_unsupported when the job runs Refused before any structural read, naming the window. Above it, the reader is behind the translator that wrote it.
ir_version_retired when the job runs Refused before any structural read, naming the window. Above it, the reader is behind the translator that wrote it; below it, the record is retired and the message names the translator version to re-emit with.
session_steps_mismatch when the job runs Refused naming both depths. A record unrolled to two steps cannot answer a question about three, and answering anyway would report a proof about a circuit nobody described.
session_steps_unrecorded when the job runs A 1.0.0 record states no depth at all, so a question naming one is refused as unrecorded; asked without, it is answered at whatever depth it was built to and the report says so.
phase_not_recorded when the job runs A record carries no rails, so the obligation cannot be placed at either end of the pipeline.
colang_version_ambiguous when the job runs Which dialect a policy is in decides what every line of it means. Nothing of it is parsed, and the refusal names the lines that disagree.
colang2_construct_unsupported when the job runs Refused by name with its line, and no verdict is given. The cone is every flow that registers the action, every flow that builds a gate of its guard, and every unread line that could invoke it -- naming it, or an execute, await, start, send, run, activate or bot line; a construct outside it is listed as a warning and the verdict stands, because it cannot change the answer.
colang_unread_in_cone when the job runs The same rule as for Colang 2: an unread line is a missing edge, and a missing edge can make a policy look safe that is not. A line in a flow outside the cone is a warning and the verdict stands; a line outside every flow is in every cone.
graph_schema_invalid when the job runs The export is what is verified, so one the reader cannot hold to its schema is refused before anything is compiled, naming the first violation.
graph_catalogue_mismatch when the job runs Refused naming the tool. A tool the catalogue does not declare has no check, no confirmation wording and no classification, so nothing a verdict on it said would be about the tool that runs.
graph_recursion when the job runs Subgraphs are inlined at each use, and one that contains itself has no depth to inline to.
graph_too_large when the job runs Subgraphs are inlined at each use, so a graph is as large as it is once inlined: a subgraph used twice at each of twenty levels is a million nodes from forty declared. Refused above 4096 inlined nodes, naming the node that passed the bound.

The client's exceptions

The Python client turns each error into an exception carrying what you need to act on it:

Exception When What it carries
LicenceRequired no API key, or no licence activated on this machine names stalwart licence activate and where to get a key
SelectorUnavailableError a plan or a tool check with no runtime and no router_url which of the two to set up
PersonalDataError personal data in a request bound for our servers; nothing was sent .findings: each match's kind and where it is, never its value
ConfigurationError a bad base URL, or a runtime that is not there
AuthenticationError 401
NotEntitledError 403 .entitlement: the grant the key lacks
RefusedError 409 refused .slug, .explanation, .stage
QuotaExceededError 429 on a monthly meter; never retried .meter, .used, .limit, .resets_at
ConcurrencyLimitError, RateLimitError a 429 that clears by itself, raised after the retries .current, .limit
InputTooLargeError 413 .limit_bytes
JobFailedError a job that ended failed .cause, .stderr_tail
DigestMismatch a download that does not hash to its digest
StreamError a stream that cannot be resumed
ServiceError 5xx, after the retries .request_id

A verdict is never an exception: SAT and STALL are answers (chapter 6). The client retries reads, plans and checks on a dropped connection, a timeout, a 5xx and a 429 that clears by itself, honouring Retry-After. A solve is submitted under an idempotency key the client derives, so a retried submission is one job.

The agent package raises its own errors, each saying what to change in the words of agent.yaml (chapter 30).

Troubleshooting

The failures teams meet most often, the commonest first.

  1. A stream arrives all at once, at the end. A proxy between you and us is buffering the response: a two-second solve looks the same buffered or not, so it shows only on long ones. Turn buffering off for the path, or let the client fall back to polling, which it does by itself after two failed streams (chapter 9). stalwart doctor checks whether streaming gets through.
  2. SelectorUnavailableError on a plan or a tool check. Plans and checks run on your own machine: activate the licence once (stalwart licence activate), or name your enterprise router with QLS_ROUTER_URL. Our hosted service answers neither (chapter 38).
  3. LicenceRequired. Nothing in the package works without a key and an activated licence, except activating it, stalwart doctor and checking a certificate. Set QLS_API_KEY, then run stalwart licence activate once on the machine.
  4. PersonalDataError. Something bound for our servers, such as a catalogue description, a rule sheet or a prompt, holds an e-mail address, a telephone number, a card number or another identifier, and nothing was sent. The error names the kind and the place; take the value out, or list your own identifiers with own_identifiers= if the match is yours to send.
  5. A verdict of SAT read as a failure, or a failing table read as a pass. A finding is a successful job: gate on verdict.status and on a table's hard_failures, never on the HTTP status or the exit code (chapter 11).
  6. 409 refused with colang_unread_in_cone. The compiler could not read a line the question can reach, and a verdict over it would mean nothing. The refusal names the line (chapter 6).
  7. 403 not_entitled or not_in_tier. Your key lacks the grant, or your plan the feature. The body names which; chapter 40 says what each plan includes.
  8. 429 quota_exhausted. A monthly meter is spent. The body names it and its reset date; the console's Usage page shows each meter against its cap.
  9. 410 artifact_purged. The artefact was deleted at the end of its retention window. Its digest and its record remain; re-run the job to get it again (chapter 16).
  10. 409 idempotency_key_reused. The same idempotency key was sent with a different request. Use a new key for a different request; the same key with the same request returns the existing job.

Two failures that tell you little, on purpose

  • The console's sign-in failure is one page with one sentence, whatever the cause: an expired link, an unknown address, a revoked key, a test key. Telling them apart would tell someone guessing which part of their guess to keep (chapter 5).
  • The API reference's Test Request panel sends a request only to the host that served the page. A server edited in the panel to point anywhere else is refused by the page's security policy, and the panel says nothing about why (chapter 18).

The limits

  • An error says what failed and what to change; it does not say what the run would have found.
  • A refusal is about the combination you asked for, not about your policy.