Skip to content

EndpointDriver::drop panics on a poisoned mutex, escalating any accept-loop panic to a process abort #723

Description

@yyyyy3155550

Describe the bug

When the endpoint accept loop panics for any reason, the panic poisons the EndpointInner state mutex. During unwinding, impl Drop for EndpointDriver runs and calls self.0.state.lock().unwrap() (noq 1.0.0, noq/src/endpoint.rs:495), which panics again on the PoisonError. A panic while already unwinding through a destructor is a non-unwinding double panic, so the whole process aborts with SIGABRT (exit 134) instead of just losing the endpoint:

panic in a destructor during cleanup.
thread caused non-unwinding panic. aborting.

So any recoverable panic in the EndpointDriver task escalates to a hard crash of the entire host process. This is the same fatal mechanism noted in #465 ("cleanup hits poisoned mutexes and the process aborts"); #465's fix was for a specific first-panic cause, but the underlying Drop-poison-abort path is still present in 1.0.0.

We hit it in production via a relay disconnect, where the first panic is the active_connections -= 1 underflow at noq/src/endpoint.rs:648. That underflow is fixed on main by #717 (merged, but unreleased as of noq-v1.0.0). This issue is specifically about the second panic — the Drop impl aborting the process — which #717 does not address and which would still turn any other accept-loop panic into a full crash.

Version info

  • noq 1.0.0 (tag noq-v1.0.0, 2026-06-15)
  • iroh 1.0.0 (2026-06-15), used as a long-running receiver/endpoint
  • Reproduced on macOS and Windows

Stack trace

thread '<iroh accept loop>' panicked at noq-1.0.0/src/endpoint.rs:648
    attempt to subtract with overflow            // active_connections -= 1 (fixed by #717)

thread '<iroh accept loop>' panicked at noq-1.0.0/src/endpoint.rs:495
    called `Result::unwrap()` on an `Err` value: PoisonError { .. }
    in <noq::endpoint::EndpointDriver as core::ops::drop::Drop>::drop

panic in a destructor during cleanup.
thread caused non-unwinding panic. aborting.

Trigger

A relay-server disconnect during normal operation:

Lost connection to relay server ... peer closed connection without sending TLS close_notify

This drives the connection into draining/teardown, hits the active_connections underflow (first panic, #717), then the poisoned-mutex unwrap() in EndpointDriver::drop (second panic) aborts the process. The Drop behaviour is independent of what caused the first panic, so #717 alone does not prevent the abort for other first-panic causes (cf. #465, #684, iroh#4289).

Expected behavior

A panic in the EndpointDriver task should at worst tear down the endpoint and surface as errors on the noq/iroh APIs (cf. #684), never abort the whole process. The Drop impl should not unwrap() a poisoned lock — e.g. recover via .lock().unwrap_or_else(PoisonError::into_inner), or otherwise guard the destructor so it can never panic.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    🚑 Needs Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions