Fail-closed validation: do not skip rules on error

When a block or an unreachable gate fails the request instead of silently falling back to the provider.

Fail-closed means a validation block or an unreachable gate fails the request instead of silently skipping rules. Prefer this for money, PHI, and irreversible tools. Fail-open (deliver anyway) is a conscious risk, not the default for consequential actions.

Problem

Teams often add a "fallback to the provider" when the proxy errors. That path bypasses every rule. The dashboard still looks green. The ledger write still happens.

Forces

  • Availability pressure pushes direct-provider fallbacks.
  • Compliance needs a clear story when the gate is down.
  • Streaming UX fights buffering on block / reask.

Pattern

Put ValGuard on the request path. On validation block, return the block to the client and route to human or safe default. On ValGuard unreachable, fail the call unless the product owner explicitly accepts a documented bypass that drops protection. See Trust.

flowchart LR
  C[Client] --> V[ValGuard]
  V -->|pass| M[Model then next step]
  V -->|block| H[Escalate]
  V -->|unreachable| X[Fail request]

Do not imply a per-agent toggle can keep rules while skipping an unreachable proxy. A direct provider call has no ValGuard rule IDs.

Code

Branch on block (OpenAI SDK raises on HTTP 403 with validation_block):

import openai
from openai import OpenAI

client = OpenAI(
    base_url="https://api.valguard.ai/v1",
    api_key="vg_live_...",
    default_headers={"X-VG-Agent": "refund-guard"},
)

try:
    raw = client.chat.completions.with_raw_response.create(
        model="openai/gpt-4o-mini",
        messages=[{"role": "user", "content": "Refund 500 on order 12"}],
    )
except openai.PermissionDeniedError as error:
    if isinstance(error.body, dict) and error.body.get("type") == "validation_block":
        # escalate — do not call the payout API
        raise SystemExit("blocked")
    raise
print(raw.parse().choices[0].message.content)

Trade-offs

BenefitCost
Side effects do not run on bad outputHard dependency on the gate
Clear audit storyMust design client retries carefully
Matches regulated buyer expectationsStreaming block/re-ask buffers the full reply

Anti-patterns

  • Silent fallback to the provider on any ValGuard error.
  • Parsing free-text errors instead of status / validation_block.
  • Mixing shadow and enforce without knowing which agents still deliver on would-block.

Related

Next step

Document your fallback owner. If you keep a direct-provider escape hatch, label it as unprotected in runbooks and Trust.