SLICE-4: harden mailer failure path (send-before-commit + 502 delivery_failed) #7
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Harden the mailer failure path when
SmtpMailerlands (SLICE-4)Surfaced by the SLICE-2 final pre-merge review (PR #6). Harmless in SLICE-2 because
LogMailernever fails, butSmtpMailer(SLICE-4) will raise on a refused/unreachable relay, so SLICE-4 must close two gaps. SD-0001 scopes502 delivery_failedto SLICE-4, so this is the natural home.I-1 —
request_codecommits the code before dispatching itbackend/app/domains/accounts/service.py—request_codedoesconn.commit()(code row durable) and thenmailer.send(...). Ifsend()raises, the code is committed withconsumed_at IS NULL, so the 60s resend cooldown fires on the user's next attempt — they're honestly told the code didn't go out (INV-9) but cannot immediately request a fresh one, violating §6.9 ("codes are re-requestable").Fix (SLICE-4): send-before-commit (and roll back on failure), or delete the just-inserted row on
send()failure before re-raising. Mind holding the txn across the SMTP call vs. cleanup-on-failure — pick the simpler correct shape.I-2 — BFF has no
502 delivery_failedhandlerbackend/app/main.py— therequest-codehandler has noexceptfor a mailer delivery error, so a raisedsend()would yield a 500, not the502 delivery_failedenvelope §6.4 specifies (INV-9). TheMailerport docstring already promises "Raises on delivery failure (INV-9 → §6.4 502)".Fix (SLICE-4): define a delivery-failure exception the port raises, catch it in
request_code, return_error(502, "delivery_failed", ...). Add a test (a stub mailer that raises) — the §6.6 PPE rehearsal also exercises real delivery.Also worth a look in SLICE-4 (minor, MVP-accepted today)
request_codecooldown check isn't serialized — two concurrent requests for one email can both issue a code (low probability; not a security issue, allows more codes not fewer).retry_after_sin the JSON body (per §6.4) but noRetry-AfterHTTP header (RFC 6585) — add the header for API clients.Context
slice-2-identity→main@1b40bde(PR #6).wiggleverse-ecomm-content/specs/SD-0001-mvp-sign-up-and-single-storefront.md§6.4, §6.9, §7.2 SLICE-4.