Skip to content

Contact form: a sendmail that exits before reading it all has not sent it - #579

Merged
adamjohnwright merged 1 commit into
mainfrom
fix/sendmail-unread
Oct 21, 2026
Merged

adamjohnwright merged 1 commit into
mainfrom
fix/sendmail-unread

Conversation

@adamjohnwright

@adamjohnwright adamjohnwright commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

A single-PR fix: the contact form's mail hand-off. It is not part of any spec.

The bug. sendmail resolved on the child's exit code alone. A sendmail that exits 0 before reading the whole message makes two events race:

  • the EPIPE on its stdin, which rejects;
  • the child's close, which resolved.

Whichever came first decided the result, so a message cut short could be reported as sent. The spec "fails, rather than taking the server down, when the MTA does not read it all" failed on CI twice on 20 Oct (#573, #578) while passing locally.

The fix. An exit of 0 counts as success only when stdin was also written in full (writableFinished). Both orders now reject.

How far this is checked.

  • The review reproduced the race with stand-in scripts (exit 0, reading nothing). On the old code, 3 to 14% of 1 MB sends and about half of 70 KB sends were wrongly reported sent. With this change, 0 of 104 1 MB sends were.
  • A healthy stand-in (cat >/dev/null; exit 0) resolved every time: about 3,900 sends from 0 bytes to 10 MB, with the CPU loaded too. stdin's finish always came before the child's close.
  • The race would not show reliably on this host (the old code passed every attempt), so no new spec: one that passes on both versions would prove nothing. The existing spec passes 28/28.
  • One limit no parent-side check can close: a message small enough to fit in the pipe whole is written even if never read. A real sendmail -t -i reads to the end of input, so this doesn't arise in practice.

🤖 Generated with Claude Code

…t it

sendmail resolved on the child's exit code alone. A sendmail that exits 0
before reading the whole message makes two events race: the EPIPE on its
stdin (rejected) and the child's close (resolved). Whichever came first
decided, so a message cut short could be reported sent, and the spec
"fails, rather than taking the server down, when the MTA does not read it
all" failed on CI twice on 20 Oct (passing locally). Now an exit of 0 is
success only when stdin was also written in full; both orders reject.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@adamjohnwright
adamjohnwright merged commit c6f7760 into main Oct 21, 2026
7 checks passed
@adamjohnwright
adamjohnwright deleted the fix/sendmail-unread branch October 21, 2026 00:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant