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:
| Signal | How it shows | What it means |
|---|---|---|
action: "block" | The wrapped call raises GuardBlockedError or *verexa.BlockedError; a direct guard call returns the verdict | The turn stopped: the prompt never reached the provider, or the reply never reached your user |
degraded: true | degradedDetectors names the detectors that did not run | A 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: true | No 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 balanced | The 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
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
degradedDetectorsnext 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
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.
{ "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.
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.
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
- Core concepts: the fields behind degradation and fallbacks
- Guardrails: which detector you lost when a check degrades
- Policies: run detectors in monitor mode while you tune them
- Events and traces: follow one turn through the dashboard