TokenPolice
Docs
Core concepts

Fail-open

The golden rule: TokenPolice never throws into your app except one designed case.

Golden rule: TokenPolice never breaks your app. A firewall that takes your app down when it has a problem is worse than no firewall — so if anything goes wrong on TokenPolice's side, the SDK swallows it and lets your LLM call proceed.

What "fail-open" means in practice

If the collector is slow, unreachable, misconfigured, or returns an error — a timeout, a network blip, a bad API key — the SDK does not raise. It logs the problem (if you asked it to) and your call runs exactly as it would without TokenPolice. Your users never see a TokenPolice error because of TokenPolice's trouble.

The one exception

There is exactly one error the SDK will ever throw into your code: TokenPoliceBlockedError. It's raised only when a rule you configured decides to block and both dials are set to enforce (dry-run vs enforce). That's not a failure — it's the firewall doing the job you asked for.

try:
    reply = run_agent(...)
except tp.TokenPoliceBlockedError as e:
    # a rule you set is enforcing and chose to block this call
    return "You've hit your usage limit for now."

The error carries structured fields so you can react precisely: reason, rule_id, kind (which rule type fired), and trace_id. Full contract in Errors.

TokenPoliceBlockedError is a deliberate block, not a fault. Everything else — every timeout, outage, or config mistake — is silently allowed through. If you're seeing this error, a rule wanted to stop the call.

One deliberate exception, the other way round: on a long-running server, a block already in force stays in force. If a user or conversation has already been blocked and TokenPolice then goes unreachable, that block holds under enforce — an outage can't be used to lift a limit you set. New decisions still fail open.

This depends on the SDK holding an open stream of rule and blocked-set updates, which it does only when deployment resolves to "daemon". On "serverless" and "edge" there is no stream — every decision is an inline /check, so during an outage everything fails open, including a block that was already in force. If that matters for your workload, keep the enforcing path on a daemon deployment.

Next