Skip to main content

Handling Policy Blocks and Gateway Errors

Gateway-generated failures use a consistent public error envelope. An upstream provider response may instead be preserved so that its status and provider-specific details are not lost. This page separates the stable Keeptrusts errors from those provider responses.

Public error envelope

Keeptrusts returns JSON in this shape:

{
"error": {
"message": "Request blocked by policy: prompt_injection.detected",
"type": "invalid_request_error",
"param": null,
"code": "content_policy_violation"
}
}

Use error.type and error.code for program logic. Treat error.message as user-facing text that may be more descriptive.

Common error categories

SituationStatuserror.typeerror.code
Buffered request or response blocked by policy400invalid_request_errorcontent_policy_violation
Header/body session ID mismatch422invalid_request_errorsession_id_mismatch
Missing, invalid, revoked, or expired API token401authentication_errorinvalid_api_token or tokens.expired
Token budget exhausted402cost_budget_exceededtokens.budget_exhausted
Token request limit exhausted403access_deniedtokens.request_limit_exhausted
Gateway-generated provider failureroute-specific 4xx or 5xxprovider_errormay be null
Gateway runtime failure500internal_errormay be null

Policy block example

For a non-streaming request, a policy block returns HTTP 400 and the standard envelope:

{
"error": {
"message": "Request blocked by policy: prompt_injection.detected",
"type": "invalid_request_error",
"param": null,
"code": "content_policy_violation"
}
}

Handle this case as a governed refusal, not as a transport failure.

For a streaming request, the HTTP stream may already have started before an output policy blocks the response. In that case the status remains 200 and the policy error is delivered in the SSE body. Inspect the stream events instead of relying only on the initial status.

Invalid request example

Validation failures usually mean the payload shape or request options are wrong:

{
"error": {
"message": "Body session_id must match the configured session header when both are present",
"type": "invalid_request_error",
"param": null,
"code": "session_id_mismatch"
}
}

Fix the request and retry only after the client payload is corrected.

Provider failures

Provider failures are deliberately less uniform. Depending on the request family and where the failure occurs, the gateway either preserves the upstream provider's status and error body or creates a provider_error envelope. Do not assume a provider-specific message or code will be present.

Treat a provider failure as retryable only when the status is retryable, the operation is idempotent, and the surrounding workflow can tolerate delay. Never turn a failed provider call into a silent success.

How to debug the failure

  1. Reproduce the request while kt events tail --since 10m --json is running.
  2. Narrow to blocked decisions with kt events tail --since 10m --verdict blocked --json when the failure is policy-related.
  3. Treat the matching Event and request ID as authoritative. Open History only when capture is enabled and you need retained request/session detail.
  4. Use Trail or exports when the failure needs an evidence package for handoff.

Application guidance

  • Branch on error.type and error.code, not on free-form message text.
  • Do not retry invalid_request_error or content_policy_violation blindly.
  • For provider errors, consider both the HTTP status and the returned body; the body may be provider-native.
  • Retry only when the request is idempotent and your caller can tolerate delay.
  • Log the request identifier and the returned error fields together so later investigation can join application logs to Keeptrusts evidence.

Next steps