---
title: "Never use assertions for checks production · Agent Wranglers"
url: https://agentwranglers.se/playbook/architecture/errors-and-nulls/no-assert-in-production-checks
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Never use assertions for checks production needs

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Errors and nulls](https://agentwranglers.se/playbook/architecture/errors-and-nulls.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

- ID: ARC-69
- Level: Required
- Chapter: [Errors and nulls](https://agentwranglers.se/playbook/architecture/errors-and-nulls)
- Rule: Write every check on input, permissions or state with an explicit condition that raises an error. Use `assert` only for facts that tests should catch, never for checks production depends on.
- Risk: An account or system taken over
- How: Several runtimes strip assertions from production builds. Python removes every `assert` under the `-O` flag. Java skips them unless the JVM starts with `-ea`. PHP skips them when `zend.assertions` is `-1`, the setting its production config recommends. An access check written as an assertion then lets every request through. Raise a [typed domain exception](https://agentwranglers.se/playbook/architecture/errors-and-nulls/domain-exceptions) instead, and refuse access when the check fails, as [Deny access when a check fails](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/fail-closed) says.
- Check: A linter fails on `assert` outside test files. Examples are Ruff and Bandit rule `S101` for Python and a grep for `assert` in PHP and Java source folders. The test suite also passes with Python's `-O` flag, or with Java assertions off.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### Python example

```python
# Bad: under python -O this line is gone, and anyone can refund
def refund(order: Order, user: User) -> None:
    assert user.can_refund(order), "not allowed"
    payments.refund(order)

# Good: the check runs in every build
def refund(order: Order, user: User) -> None:
    if not user.can_refund(order):
        raise RefundNotAllowed(order.id, user.id)
    payments.refund(order)
```

### Java example

```java
// Bad: the JVM skips this line unless it starts with -ea
assert user.canRefund(order) : "not allowed";

// Good: the check runs in every build
if (!user.canRefund(order)) {
    throw new RefundNotAllowed(order.id(), user.id());
}
```

## Sources

- [Python language reference: the assert statement](https://docs.python.org/3/reference/simple_stmts.html#the-assert-statement)

## Related rules

- [Use typed domain exceptions](https://agentwranglers.se/playbook/architecture/errors-and-nulls/domain-exceptions) in Architecture
- [Deny access when a check fails](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/fail-closed) in Security testing

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/architecture/errors-and-nulls/no-assert-in-production-checks">Agent Wranglers architecture playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Cap every collection that grows with use](https://agentwranglers.se/playbook/architecture/errors-and-nulls/bounded-collections)
- [Errors and nulls](https://agentwranglers.se/playbook/architecture/errors-and-nulls)
