Skip to content

Fix: a stray QUIC handshake no longer kills a direct-path send - #77

Merged
op-q merged 1 commit into
mainfrom
fix/accept-survives-stray-handshakes
Sep 16, 2026
Merged

op-q merged 1 commit into
mainfrom
fix/accept-survives-stray-handshakes

Conversation

@op-q

@op-q op-q commented Sep 15, 2026

Copy link
Copy Markdown
Owner

Bug in shipped releases (v0.2.0+), independent of the other open PRs. Based on main, so it can merge on its own. It's a behaviour change, so it waits for your review.

The bug

A sender on the direct path waits in QuicEndpoint::accept_transfer. If anything reached its UDP socket first with a QUIC handshake that failed, accept_transfer returned that error and the whole send ended. The real receiver then timed out.

This is the netlab authentication failed mystery. A traced failing run:

iroh::_events::conn::incoming: remote_addr=Ip(10.40.0.2:7842)   ← the relay's address-discovery port
noq_proto::endpoint: failed to authenticate initial packet
error: a peer failed to complete the handshake: ... authentication failed

A late packet from address discovery was taken for an incoming connection. The same thing is reachable on purpose: one UDP packet to a waiting sender's public address ends its transfer.

The fix

A failed handshake is dropped and the sender keeps waiting. It can't be the receiver, which completes the handshake, and it isn't a guess, which needs a completed handshake first, so the one-guess rule (decisions 13 and 18) is untouched.

Evidence

  • netlab plain LAN: main passed 1 run in 5; with this fix it passed 8 in 8.
  • New unit test: sends a junk QUIC v1 Initial to a waiting sender, then connects a real receiver, which must get a live stream. Negative control: with the old behaviour the test fails 3 out of 3. A race in the first version of the test was found and fixed; it now passes 5 times in 5 in parallel with the other QUIC tests.
  • fmt, clippy -D warnings and all 181 tests on main pass.

The root cause is recorded in the netlab plan.

🤖 Generated with Claude Code

https://claude.ai/code/session_01G7Fy45hUvna94cd79WKG8S

A direct-path sender ended its whole transfer if anything reached its socket
first with a QUIC handshake that failed. The network lab traced the
intermittent `authentication failed` to exactly that: a late packet from a
relay's address-discovery port was taken for an incoming connection, failed,
and ended the send before the real receiver dialled. Plain LAN passed 1 run
in 5 on main. On a public address, anyone who can send one UDP packet could do
the same on purpose.

A failed handshake is now dropped and the wait goes on. It is not the
receiver, which completes the handshake, and not a guess, which needs a
completed handshake first, so the one-guess rule is untouched.

After the fix, plain LAN passed 8 runs in 8. A unit test sends a junk QUIC
Initial to a waiting sender and then connects a real receiver; with the old
behaviour it fails 3 times in 3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G7Fy45hUvna94cd79WKG8S
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

This was referenced Sep 15, 2026
@op-q
op-q merged commit f6c4772 into main Sep 16, 2026
6 checks passed
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