Handle failures

Decide what your app does when a turn is blocked, a detector degrades or the API is unreachable.

Failures reach your app in a few distinct shapes, and each one wants a different response. A block stops a turn on purpose, a degraded check answers with fewer layers than you asked for, and an outage produces no verdict at all. This guide covers what each one looks like at the call site and what the SDKs already do for you.

  • A blocked turn never reaches your model or your user, and a wrapped client raises instead of returning
  • A degraded check is a real verdict built from the layers that ran, with the skipped detectors named in degradedDetectors
  • A fallback is the SDK's own verdict when no answer arrived, with an action set by your fail mode
  • A failed judge leaves the verdict to the cheaper layers, and audit pipelines should look for it

Failure signals at a glance

Every failure is visible in the verdict itself, so you never have to infer one from a log line:

SignalHow it showsWhat it means
action: "block"The wrapped call raises GuardBlockedError or *verexa.BlockedError; a direct guard call returns the verdictThe turn stopped: the prompt never reached the provider, or the reply never reached your user
degraded: truedegradedDetectors names the detectors that did not runA detector that should have run was skipped, usually over budget or because the classifier was unavailable
planHash: "unavailable"An empty detectors list, latencyMs: 0, degraded: trueNo verdict arrived and the SDK returned a fallback
A tier-3 stage of "failed"No judge object on an audit response, or a later trace event on balancedThe judge did not run; the verdict rests on the cheaper layers

Handle a blocked turn

A block verdict stops the turn. A blocked prompt is never sent to your model, and a blocked reply is never handed to your user. With a wrapped client the call raises instead of returning: GuardBlockedError in Python and TypeScript, *verexa.BlockedError in Go. The error carries the phase and the full verdict, so you can see which detector fired.

If you call check_input, checkInput or CheckInput yourself, nothing raises — a block is just an action to branch on, as in the Quickstart.

  • Refuse the prompt in whatever words fit your product. Resending the same text will be blocked again
  • Drop or retract the reply. Streamed replies are checked when the stream ends, so chunks may already be on screen; treat the error as the signal to retract them
  • Keep the trace. Log the phase and the detector ids, and use the trace id to find the turn in the dashboard
React to a blocked turn
from verexa import GuardBlockedErrortry:    completion = client.chat.completions.create(model="gpt-4o", messages=messages)    send(completion.choices[0].message.content)except GuardBlockedError as err:    log.warning("blocked on %s: %s", err.phase, err)    if err.phase == "output":        retract_reply()  # streamed chunks may already be visible    reply("Sorry, I can't help with that.")

Only block raises. A flag verdict continues the turn and is recorded for review, and a redact verdict continues with the rewritten text.

Work with degraded verdicts

degraded: true means Verexa could not run everything the profile asked for. A detector that should have run was skipped, usually because the budget ran out before its turn or because the classifier was unavailable. degradedDetectors lists exactly which ones did not run.

Only a detector that should have run can degrade a check. One that your policy disabled, or one that does not apply to the phase, is simply not in the plan.

A degraded verdict is still enforced. It is built from the detectors that ran, and a block or redact from a rule-based detector is a match, not a guess — losing a layer never turns it into an allow. Verexa also does not cache a degraded verdict, so checking the same text again really does run the full plan again.

  • Treat it as a verdict. Most apps record degradedDetectors next to the trace id and carry on
  • Retry when coverage matters. The same text and profile will re-run every layer instead of hitting the cache
  • Stay cautious on output. If a skipped detector is one you rely on for replies, hold the reply and re-check instead of showing it
Degraded still enforces
The detectors that ran keep their full authority, and only the skipped layers are missing.

When the judge cannot run

A failed judge is not reported as degraded. It is reported as a missing judge, because the only thing it changes is the coverage of the verdict you already have.

With audit, the judge runs in the path of the response. If it cannot be reached or runs out of time, the response carries no judge object and its tier-3 stage has a status of failed; you are served the verdict the cheaper tiers reached. The failure alone does not mark the check degraded.

With balanced, the verdict never waits for the judge. The response reports it as pending, and a failed judge lands in the trace as a separate event with an action of unavailable, long after your check returned.

If your pipeline treats every audit verdict as judge-reviewed, check the judge field before you rely on that. Otherwise a failed judge only narrows coverage — it never decides anything by itself.

Handle an API outage

When no verdict can be fetched at all, the SDKs return a fallback instead of throwing. That covers a missing or invalid API key, a network or DNS error, a timeout, a 5xx and an unparseable response. The fallback carries degraded: true, a plan hash of unavailable and an empty detector list, and its action comes from your fail mode.

A fail-open fallback verdict
{  "action": "allow",  "score": 0,  "detectors": [],  "text": "my email is jane.doe@example.com",  "latencyMs": 0,  "degraded": true,  "planHash": "unavailable",  "cached": false,  "stages": []}

By default the SDKs fail open: the fallback is allow, so your app keeps working while Verexa is unreachable. Fail closed makes it block, so no text gets through unchecked.

Fail closed
from verexa import create_guardguard = create_guard(fail_mode="closed")

Fail closed is the right call when a missed block is worse than downtime. Keep fail open when checks are advisory and availability matters more. With fail closed and a wrapped client, the fallback's block raises the same blocked error — read degraded on the response to tell an outage-block from a real one.

An outage should also not add a timeout to every request. After 5 failed checks in a row, the SDK returns the fallback immediately for 30 seconds before trying the API again. A successful check resets the count. Network errors, timeouts, 5xx and unparseable responses count toward it; a 4xx does not, because the service answered and the key or request is wrong.

The default timeout is 2 seconds, set per client or per check. That covers deterministic and balanced, but an audit check can spend up to 8 seconds, so it fails open before the verdict arrives. Raise the timeout to 15 seconds for audit traffic; Core concepts shows the option for each SDK.

A fallback allow looks like a clean allow
Code that only reads action cannot tell a fallback from a genuine allow. Read degraded, or watch for planHash: "unavailable", when your app needs to know the check really ran.

Find failures in the dashboard

  • Events marks degraded checks with a degraded icon in the list and a badge on the trace. Filter by action, phase, profile, detector or time range, or search for the trace id from your logs
  • The top bar shows the status of the data plane, classifier, judge and cache; an amber classifier or judge pill explains most degraded checks at a glance
  • Overview tracks the share of checks that were degraded, so a spike after an outage is visible without opening a trace

Next steps