---
title: "Stop on permanent failures and retry · Agent Wranglers"
url: https://agentwranglers.se/playbook/email/bounces-and-complaints/handle-bounces
id: EML-32
level: Recommended
applies: email
updated: 2026-10-21
build: a2a6bdfed830
tokens: 856
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Stop on permanent failures and retry temporary ones

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Bounces and complaints](https://agentwranglers.se/playbook/email/bounces-and-complaints.md).

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

- ID: EML-32
- Level: Recommended
- Chapter: [Bounces and complaints](https://agentwranglers.se/playbook/email/bounces-and-complaints.md)
- Rule: Read every bounce and SMTP reply. Suppress an address after a permanent failure for a bad mailbox, and retry only temporary failures.
- Check: Send to an address that does not exist. The bounce is recorded against the message, and the address is suppressed. A test bounce with `5.7.1` raises an alert and leaves the address active.
- Applies when: The project sends email.
- How: SMTP reply codes starting with 5 are permanent, and those starting with 4 are temporary (RFC 5321). Enhanced status codes add detail (RFC 3463). `5.1.x` means the address is bad, so suppress it. `5.7.x` means the receiver refused you for policy or authentication. Fix your sending instead of dropping the address. Parse delivery status notifications (RFC 3464) and match them to the message, for example with a unique return path per message (VERP).

## 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.

### Plain text example

```text
550 5.1.1 <nobody@example.com>: Recipient address rejected: User unknown
550 5.7.515 Access denied, sending domain does not meet the required authentication level
451 4.7.1 Try again later
```

### JavaScript example

```js
function bounceAction(enhancedCode) {
  if (enhancedCode.startsWith('4.')) return 'retry';
  if (enhancedCode.startsWith('5.1.')) return 'suppress-address';
  return 'alert-sender-team';
}
```

### Python example

```python
def bounce_action(enhanced_code: str) -> str:
    if enhanced_code.startswith("4."):
        return "retry"
    if enhanced_code.startswith("5.1."):
        return "suppress-address"
    return "alert-sender-team"
```

## Sources

- [RFC 5321, SMTP, section 4.2.1](https://www.rfc-editor.org/rfc/rfc5321#section-4.2.1)
- [RFC 3463, Enhanced Mail System Status Codes](https://www.rfc-editor.org/rfc/rfc3463)
- [RFC 3464, Delivery Status Notifications](https://www.rfc-editor.org/rfc/rfc3464)

## Related rules

- EML-22 [Keep the list clean](https://agentwranglers.se/playbook/email/deliverability/list-hygiene.md) in Email
- STK-73 [Use Amazon SES to send transactional email](https://agentwranglers.se/playbook/stack/product-services/ses-transactional-email.md) in Software choices

**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/email/bounces-and-complaints/handle-bounces">Agent Wranglers email playbook</a>`. In code you write, add a comment next to it with the same link.

- Next: [Retry with backoff and give up in time](https://agentwranglers.se/playbook/email/bounces-and-complaints/retry-and-expire.md)
- [Bounces and complaints](https://agentwranglers.se/playbook/email/bounces-and-complaints.md)
