PlanCover Docs
Reference

Errors

One error envelope for HTTP, one error event for streams.

Failures are explicit. The alternative — an assistant that smooths over a failed tool call with a plausible sentence — is the single worst outcome for an insurance product.

HTTP errors

Every non-streaming failure returns the same envelope, carrying the request id so a report can be traced:

{
  "error": {
    "code": "http_error",
    "message": "Not Found",
    "request_id": "f792521fd91a42e49c90d1da3f1c94b4",
    "details": null
  }
}

Responses are JSON with a real status. A 429 also carries Retry-After (whole seconds) and retry-after-ms.

StatusCodeMeaning
401invalid_api_keyThe API key is missing, unknown, expired, revoked, or for the other environment (pcai_live_ is prod only, pcai_test_ everywhere else)
401unauthorisedThe X-PlanCover-Session member session is missing where required, or malformed (a session id never expires), or a sign-in code is wrong or expired
403permission_deniedThe API key's role may not call this route
403forbiddenNot permitted — another member's confirmation or conversation, or one no longer waiting
422validation_errorThe body failed validation; details lists the fields
429rate_limitedOver the API key's or the member's limit. Wait Retry-After seconds
503upstream_unavailableA dependency, the database included, is unavailable. No turn ran; retry
500internal_errorAnything unhandled

A chat turn runs every one of these checks before its stream opens, so a refused turn is one of these responses and never a stream.

Stream errors

Once a stream has started, an HTTP status is no longer available. Failures arrive as an error event and terminate the stream:

{ "code": "upstream_timeout", "message": "…", "retryable": true }
CodeMeaningretryable
upstream_timeoutA node exceeded its ceilingYes
upstream_unavailableA dependency failed or timed outYes
stream_interruptedThe stream ended without a terminatorYes
loop_limitThe graph hit its recursion backstopNo
unauthorisedA session check failed after the stream openedNo
forbiddenNot permitted — including an approval that is not the caller's to giveNo
rate_limitedToo many messages for this member. Retry after a wait, not immediatelyNo
internal_errorAnything unhandledNo

The key, the session, the quotas and a confirmation are all checked before the stream opens, so unauthorised, forbidden and rate_limited normally arrive as the HTTP statuses above. As frames they mean the turn was refused part-way.

A failed tool call is not one of these. It completes the stream normally and reports itself on tool.completed, with one of the stable tool failure codes. An error frame means the turn itself ended.

Partial answers

An error after some text has already streamed leaves that text on screen. The client marks the message as incomplete rather than deleting it — the member saw it, and pretending otherwise is more confusing than the failure.

What the member sees

Error messages name the problem and the recovery. They do not expose stack traces, internal identifiers or upstream vendor names.

On this page