Runbook: dead-letter storm
A dead letter is a terminal failure kept for replay (dead_letters, kinds invocation, outbox, inbox). Input errors
(4xx-class) are never dead-lettered; everything that is can be replayed once, atomically, from the admin API or the dashboard.
Symptoms
Section titled “Symptoms”deadLetters.pendinginGET /api/admin/overviewclimbing; the dashboard’s Dead letters page filling up.- Invocations ending
deadafter their retry policy (stayfn.events.failed{handler}rising for handlers). - Usually one cause: an OHIP outage (5xx), a bad deploy of a function, a malformed stream of events (kind
inbox), or a missing variable.
Diagnose
Section titled “Diagnose”- Group them:
GET /api/admin/dead-letters?tenantId=<id>&replayed=false&page=1&pageSize=200→kind,reason,refId,createdAt. Onereasonshared by all letters means one cause. - For kind
invocation:GET /api/admin/invocations/{refId}→ error code and the OHIP call timeline (5xx from OHIP, timeouts, or no call at all). - For kind
inbox/outbox:GET /api/admin/events?tenantId=<id>&status=deadandGET /api/admin/events/{id}(masked payload, handler invocations). - Check the upstream: the OHIP status page,
stayfn.ohip_calls{status}andstayfn.ohip.call_msin the metrics, WireMock/sandbox reachability. - Before replaying: make sure the cause is gone — a replay of a still-failing call just dead-letters again.
Remediate
Section titled “Remediate”- Fix the cause (wait for OHIP, roll back the function, correct the variable, fix the event source).
- Replay: one letter
POST /api/admin/dead-letters/{id}/replay(operator) — orstayfn replay --dead-letter <id>— creates a new invocation (correlationreplay:<letter id>) through the outbox; a letter replays once (409already_replayed). - Many letters: loop over the list from step 1 of Diagnose, oldest first, a few per second (the replays share the OHIP budget).
- Letters that should not be replayed simply age out (
Retention:DeadLetters, 90 days by default;backup-restore.md).
WireMock answered the reservation search with 503; three invocations died after their in-process retries and left three dead letters; after the mapping reset they were listed, one was inspected and all were replayed successfully.