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
| Benefit | Cost |
|---|---|
| Side effects do not run on bad output | Hard dependency on the gate |
| Clear audit story | Must design client retries carefully |
| Matches regulated buyer expectations | Streaming 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.