TL;DR — Most production guardrails are implemented as a second LLM or classifier call wrapped in a try/except that defaults to allowing content when that call times out, rate-limits, or returns garbage. Teams stress-test whether the guardrail catches bad input, but almost nobody stress-tests what happens when the guardrail itself fails. That silent fail-open default is a bigger production safety risk than most of the jailbreaks it's supposed to stop.
Every guardrail architecture I've reviewed in the last two years shares the same shape: a pre-check on the input, generation in the middle, a post-check on the output. Each check is usually its own model call — a moderation classifier, a smaller judge model, sometimes the same LLM asked to grade its own output. Teams spend real effort tuning these checks. They red-team them, they measure precision and recall on adversarial prompts, they argue about thresholds in design docs.
Almost none of them ask the other question: what does the system do when the guardrail call itself fails?
The Try/Except That Ate Your Safety Policy
Go find the code. It's usually a few lines wrapping the moderation call in a try/except, and in the except branch, generation proceeds anyway. Sometimes there's a log line. Sometimes there isn't even that. The reasoning, when anyone bothers to articulate it, is reasonable-sounding: we don't want a moderation API outage to take down the whole product.
That reasoning is correct for availability. It is a silent, undocumented policy decision for safety. Nobody wrote "if the safety layer is unavailable, ship unmoderated content to users" into a design review. It emerged from an engineer trying not to cascade one outage into two, and it became the de facto safety posture of the system without anyone deciding it should be.
This matters because a guardrail call is not a stable, always-on property of your system. It's a network request to a model, with the same failure modes as every other model call: timeouts under load, rate limits during traffic spikes, malformed JSON when the judge model decides to add a caveat before its verdict, cold starts, provider-side degradation. You've built a safety-critical control path out of the least reliable component in your stack, and then you've made its failure mode invisible by design.
Guardrails Are a Service, Not a Property
The mental model that causes this bug is treating "the guardrail" as an inherent property of the pipeline — something that's just true about your system, like a type annotation — instead of treating it as what it actually is: a separate service with its own latency distribution, its own error rate, and its own dependency chain. A type annotation doesn't degrade under load. A service does.
Once you see the guardrail as a service, its outages stop being edge cases and start being a predictable, schedulable event. It will have a p99 latency. It will occasionally 500. It will occasionally return a response that doesn't parse into the enum your code expects. If you haven't load-tested your moderation classifier the same way you load-tested your primary model endpoint, you don't actually know its failure characteristics — you just have a fallback path nobody has exercised outside of unit tests with a mocked exception.
There's a nasty correlation hiding here too. Provider-side degradation, rate limiting, and traffic spikes often happen at exactly the moments adversarial traffic is highest — a jailbreak campaign hammering your endpoint looks, from the infrastructure's point of view, a lot like a traffic spike. The conditions that make your guardrail most likely to fail are correlated with the conditions where you most need it. A fail-open default doesn't just have bad edge-case behavior; it has bad behavior precisely when the behavior matters most.
Fail Open, Fail Closed, and the Cost of Getting the Default Wrong
Site reliability engineering has a decades-old vocabulary for exactly this decision, and AI teams keep reinventing it badly. Fail open means the system defaults to permissive behavior when a check can't complete. Fail closed means it defaults to blocking. Neither is universally correct — the right answer depends on the cost of a false negative versus the cost of a false positive, and that cost is different for every risk category your guardrail covers.
A guardrail that blocks mildly off-topic chat because the moderation service is briefly unavailable is an annoying but survivable product regression. A guardrail that lets through content in your highest-severity categories — the ones your legal and trust teams actually care about — because the same service is briefly unavailable is a materially different kind of incident. Collapsing both into a single global try/except with a single fallback behavior means you've implicitly decided both categories carry the same cost. They don't.
The fix isn't "always fail closed" either. A system that hard-fails every request whenever a third-party classifier hiccups just replaces one incident with another, more visible one. The fix is tiering the decision: fail-closed defaults for the categories where a miss is catastrophic and irreversible, fail-open defaults with heavier downstream logging for categories where a miss is recoverable, and a deterministic, non-model fallback — yes, a keyword list, a regex, an old-fashioned rules engine — as the backstop for the categories that can't wait for a model call to come back.
Designing the Degrade Path on Purpose
A rules-based fallback sounds primitive next to an LLM classifier, and it is. That's exactly why it belongs in the failure path instead of the happy path. It doesn't need nuance. It needs to be fast, deterministic, and available even when every model endpoint you depend on is down. If your moderation classifier is unreachable, falling back to a blunt keyword filter for your worst-case categories is a strictly better outcome than falling back to nothing.
This also means the fallback path deserves its own test suite, separate from the one that tests whether your guardrail catches jailbreaks. You need tests that simulate the moderation endpoint timing out, returning a 429, returning truncated JSON, and returning a confident but wrong verdict — and you need to assert on what the system does in each case, not just that it doesn't crash. "Didn't crash" and "did the safe thing" are different assertions, and most guardrail test suites only check the first one.
Treat the Guardrail Like It's Also in Production
The final piece is observability. If your guardrail is a service, it needs the metrics a service gets: latency percentiles, error rate, timeout rate, and — critically — a fail-open counter that fires every time the fallback path is taken. Most teams have zero visibility into how often their safety layer is silently degrading, because the exception handler that triggers the degrade path doesn't emit anything. An error budget only works if you're tracking the errors.
Guardrails get designed like a filter: something that sits in the pipe and catches what it's supposed to catch. They should be designed like a dependency: something with an SLA, a known degrade mode, and an owner who gets paged when that degrade mode starts firing more than it should. The jailbreak your red team found in testing is a known, bounded risk you can iterate against. The guardrail quietly failing open under load is an unbounded one, because by construction, nobody's watching it happen.