Guardrails and AI Safety

Guardrails and AI Safety

Fail-closed Behavior

Fail closed behavior

54 of 57 in this book · 4 min read

What it is

The defined behaviour of a system when a safety check cannot produce a verdict — it timed out, its dependency is unreachable, its rule bundle would not load, it was rate limited, or it raised. The decision is what the absence of a verdict means for the request in progress, and it is made per check and per context in advance, because the alternative is that it is made implicitly by whatever an exception handler happens to do.

Not the failure of the thing being checked

Every other capability in this domain is about the system producing something unacceptable. This one is about the apparatus that was supposed to notice doing nothing at all, which looks like success from every direction available: the request succeeds, the latency improves, the violation counter sits at zero. A model refusing correctly while its moderation layer has been returning an empty result for nine days is a system with no moderation and a healthy dashboard, and nothing in the product's own telemetry distinguishes that from a period of unusually well-behaved traffic.

Why it matters

A check that is skipped when it fails provides exactly as much protection as its availability, and almost nobody measures the availability of a check. Worse, the conditions that break one — load spikes, a dependency outage, an unusual traffic mix, a bad deploy — correlate with the conditions under which unsafe requests arrive, so a check that degrades open is most likely to be absent precisely when it was needed. The opposite choice carries its own arithmetic: every check made a hard dependency lowers the product's achievable availability and turns a classifier restart into an outage. The work is deciding which, for each check, with the reasoning written down.

Key decisions

  • What closed means for each check: refuse the request, proceed with reduced capability, serve from the last valid policy, defer the work, or route it to a person. Refusal is the simplest answer and is rarely right for all of them.
  • Which checks are hard dependencies and which are advisory, decided from what each one protects rather than from how reliable it happens to be today.
  • The maximum acceptable staleness for cached rules, models, and bundles, and what happens at the limit rather than what happens in principle.
  • Whether a timeout is distinguishable from a clean negative anywhere in the code and anywhere in the telemetry. If it is not, every other decision here is unverifiable.
  • Behaviour under load shedding: whether checks may be dropped to preserve throughput, who may authorise that during an incident, and what is recorded about the window.
  • How a misfiring check is disabled, how quickly, and whether doing so requires a deployment.
  • What happens to work already in flight when a check fails partway through a multi-step run, especially where earlier steps had effects outside the system.
  • Whether the availability of the check path is measured, budgeted, and alerted on separately from the availability of the product.

Failure modes

  • The check is wrapped in a handler that returns a permissive result on any exception, so an outage registers as a clean period with no violations and an improved latency profile.
  • Several checks run in series, each individually reliable, and the enforced ceiling on the product is well below what any single component ever promised.
  • Closing is chosen without capacity planning, and a routine rolling restart of the classifier fleet takes the product down with it.
  • A rule bundle keeps serving because its refresh is best effort, so a rule withdrawn for legal reasons stays in force for weeks after it was removed at source.
  • The check fails after an irreversible step has already executed, and closing leaves a half-completed workflow with no compensating path and no owner.
  • The only way to disable a misbehaving check is a code change, so the available response during an incident is to turn off the entire layer.
  • A refusal produced during a fault is written to a shared cache and continues to be served to subsequent requests long after the fault was fixed.
  • Degradation is implemented on the primary path and not on retries, batch jobs, or replay tooling, which keep running with no checks at all.
  • Timeouts are logged below the level anyone alerts on, so the only visible symptom is a slow decline in the flagged rate that reads as a quality improvement.
  • The fallback path is never exercised outside incidents, so the first time it runs it has its own defect, discovered at the worst possible moment.

Related capabilities


Applies: Production readiness · Design questions