Loading…
Loading…
Postmortems / PM-2026-10-13-14
Blameless postmortem. Incident record: incident-log.md INC-2026-10-13-14. Times UTC.
The postman claims deposits on L2 by sending a claim transaction per message from one signer. During INC-13 its claim transactions went to the peer-less RPC node and never reached the sequencer; two restarts of that node dropped them. 41 messages were left PENDING with nonces 197–237 while the chain's nonce for the signer stayed at 197. The postman chooses the next nonce as max(on-chain, highest PENDING nonce + 1), so every new claim also landed above the gap and could not be mined. The postman has a repair path for exactly this — cancel the stuck nonce with a self-transfer and reset the message — but it crashed on every attempt with TypeError: Cannot mix BigInt and other types. The 41 rows were reset by hand the way the repair path would have, the postman was restarted, and it re-claimed from nonce 197.
| what | how much |
|---|---|
| W2 deposits (ETH and TERC20) | 41 not claimed on L2, up to ~1 h 50 min late (04:20 → 06:09); every message claimed exactly once in the end |
| duplicates | 2 messages got a second claim tx that reverted (MessageDoesNotExistOrHasAlreadyBeenClaimed): gas only |
| not affected | W1, W3 (L1 claims use another signer), W4, W5 |
consensys/linea-postman:1.0.1-20260629-7358b2f): the entity declares claim_tx_max_fee_per_gas / claim_tx_max_priority_fee_per_gas as bigint, but the pg driver returns int8 as a string and messageMappers.ts passes it through; ViemTransactionRetrier.cancelTransaction multiplies it by 110n → TypeError. The cancel-and-reset path can never succeed in this deployment.LinethL2MessagesStuck fired at 05:07:38 into the alert-sink log. Found by hand at 05:56 while checking all six workflows after INC-13: deposit spans had been 0 since ~04:56, 41 messages PENDING in the postman DB.
status='SENT', claim tx fields cleared, claim_cycle_count+1) in one transactionNonceManager initialized startNonce=197 onChainNonce=197 dbMaxNonce=nullCLAIMED_SUCCESS, 2 CLAIMED_REVERTED (already claimed on-chain: L2 inbox status 2)the repair copied the code's own intended reset instead of inventing one; the rows were backed up first; the source was read to explain the crash before touching the DB.
one incident's recovery (INC-13: restart the RPC node) silently caused this one; the six-workflow check that found it was done ~50 min after INC-13 was "resolved".
the token-bridge duplicates reverted harmlessly because the contract rejects a second claim.
Find why two claims were sent for the same message during the re-claim
open (after A1)
Alert when a signer's pending-in-DB nonce is ahead of its on-chain nonce for > 10 min (nonce gap)
Patch the postman: convert the two fee columns with BigInt(...) in messageMappers.ts (or a TypeORM transformer); build a lab image; test by dropping a pending claim
10-13 06:45 1fb7983: lineth-postman:1.0.1-7358b2f-lab1 (images/lineth-postman/; 439/439 postman tests, 2 new mapper tests fail without the fix). Upstream checked 10-13: no issue/PR, bug still on LFDT-Lineth/lineth-monorepo main 5c0c4caa6 (introduced with the cancel path in #2617); filing upstream not done. Live drill of the cancel path not done
After restarting a node that holds others' pending txs (RPC node), check every signer's on-chain vs DB nonce (postman L1/L2, traffic) — runbook step
— scripts/check-workflows.sh (nonce check) + docs/runbooks/node-restart.md (52b00b5)
Route alerts to a person
10-13 11:00 934a2bb: Telegram receiver on lab alerts (critical + warning, repeat 1 h); see INC-15
0xc8c9…7a62: eth_getTransactionCount latest = pending = 197 on sequencer and RPC node at 05:5x.PENDING, claim_tx_nonce 197–237, distinct, created 04:20:38–05:46:48.TypeError: Cannot mix BigInt and other types at ViemTransactionRetrier.js:56 (source: postman/src/infrastructure/blockchain/viem/ViemTransactionRetrier.ts cancelTransaction).0x80eec5…6994 for message 0xf83eb1…c918; the successful claim was nonce 225, L2 block 8630.