fix(eims): a mapper failure after reservation also orphaned the block

toEimsInvoice/buildEimsContext sat outside the try/catch that calls
settleFailure — reservation happens (TX1), then request-building ran
unguarded, then submit() was the only thing actually wrapped. Any
exception during mapping (a validation error like an unmapped buyer
country, or a bug) skipped settleFailure entirely and left the
reservation permanently held: exactly the live incident just seen —
register 500'd, and every subsequent attempt on any invoice 409'd
'already in flight' until manually resolved.

Fix: the try block now starts right after reserve(), covering
request-building and submit() both. settleFailure's determinism
check is generalized to match — any error that is not an
EimsApiException is pre-wire and safe to release, not just
EimsConfigException (still labeled CONFIG; everything else pre-wire
is now labeled the new LOCAL kind). This is exhaustive by
construction: every error that actually touches the wire is already
normalized to EimsApiException inside EimsClientService.send()'s own
catch, so nothing outside that can be ambiguous.
This commit is contained in:
Hagernesh
2026-08-17 11:53:12 +00:00
parent 0464a44de4
commit 0999bc0b8b
3 changed files with 66 additions and 31 deletions

View File

@@ -11,7 +11,8 @@ export type EimsFailureKind =
| "RULE_VALIDATION"
| "SERVER"
| "UNKNOWN"
| "CONFIG";
| "CONFIG"
| "LOCAL";
/** Raised when EIMS is disabled or its credential files are unusable. */
export class EimsConfigException extends ServiceUnavailableException {