Rust troubleshooting
Diagnose Rust connection, stream, discovery, and event problems by symptom.
Start from the observable symptom. Building an endpoint fails with a BindError (or an EndpointOptionsError from a listen setter) naming the part that failed; methods on a running endpoint return minip2p::Error. Problems found while driving arrive as events.
Common problems
| Symptom | Likely cause | What to do |
|---|---|---|
listen_on or bind fails with an EndpointOptionsError (BindError::Options) |
A listen address is not listenable (/p2p, /p2p-circuit, a DNS host, or a transport whose Cargo feature is off), or the options conflict (no listen address, two QUIC addresses for one IP family) |
The variant names the address and what was expected; fix the options. |
bind fails with BindError::Quic or BindError::Listen |
The platform refused a socket | Match the error’s IoErrorKind: AddrInUse means another process holds the port, PermissionDenied a privileged port or sandbox. |
PeerAddr fails to parse |
The address lacks a terminal /p2p/<peer-id> |
Copy the complete peer address. |
connect returns ConnectTargetError |
The address list was empty, or its addresses name different peers | Pass one peer’s complete addresses. |
ConnectSettled fails with NoUsableRoute |
No candidate had a usable shape or transport, and no relay applied | Use /udp/<port>/quic-v1 or /tcp/<port> and listen on that transport, or configure a relay. |
| A remote peer cannot connect to a printed address | The address contains 0.0.0.0 or :: |
Advertise a real interface, public, or relay address. |
| A connection exists but an application protocol cannot open | Protocol unregistered, or remote rejects negotiation | Confirm matching protocol IDs and keep both peers driving. PeerReady is optional; it only enables early RemoteDoesNotSupport. |
ProtocolNotRegistered |
The local endpoint never registered the protocol | Add .protocol("/app/name/1.0.0") or call add_protocol. |
RemoteDoesNotSupport |
The peer does not advertise the protocol version | Register the same version on both peers. |
| An optional capability reports “not enabled” | The Cargo feature exists, but the builder did not activate it | Add the builder method listed in the feature matrix. |
AutoNAT reports reachability but connect cannot relay |
Only an AutoNAT server was configured | Add a Circuit Relay v2 server with .relay(...). |
| Hole punching fails | UDP paths or observed addresses are unusable | Keep the relay path, inspect HolePunchFailed, and keep both peers driving. |
| Pubsub receives no local copy | Self-delivery is disabled | Apply the local action directly and use pubsub for remote delivery. |
| Discovery rejects a beacon | Discovery beacons must be signed | Fix the publishing peer. allow_unsigned does not apply to discovery. |
| mDNS finds no peers | mDNS is inactive, multicast is blocked, or peers do not share a link | Activate mDNS on both peers and test on a multicast-capable network. Watch for EndpointEvent::MdnsFailed. |
Event responses
| Event | Typical response |
|---|---|
EndpointEvent::PeerReady |
Record advertised protocols, or open streams if you waited for Identify first. |
EndpointEvent::StreamReady |
Attach the stream to its protocol handler. |
EndpointEvent::StreamData |
Feed the bytes into the application’s decoder. |
EndpointEvent::StreamRemoteWriteClosed |
Finish decoding and optionally close the local write side. |
EndpointEvent::StreamWriteStopped |
Stop writing: the peer refused further bytes (QUIC STOP_SENDING). Keep reading until StreamClosed. |
EndpointEvent::Error |
Record the structured context and decide whether to retry. |
EndpointEvent::MdnsFailed |
Log the MdnsError (operation, IoErrorKind, interface). mDNS keeps retrying and emits MdnsRecovered once it works again. |
RelayServerEvent::AddrsRejected |
Fix the listener or confirmed address it names. The previously accepted addresses stay in effect until AddrsRestored. |
NatEvent::PathEstablished |
Begin traffic with the ordinary stream methods. |
NatEvent::PathUpgraded |
Update path state. Existing peer operations keep working. |
EndpointEvent::ConnectSettled with ConnectOutcome::Failed |
Inspect the failure, correct the candidates or infrastructure, and retry. |
GossipsubEvent::OutboundFailure |
Retry only if the application requires it. |
DiscoveryEvent::DialFailed |
Keep discovery running. Backoff controls later attempts. |
Debug in order
- Confirm that both endpoints keep calling
waitorpoll. Each call drives only its own endpoint, so blocking on one endpoint can delay others sharing the same thread. - Log the complete peer address and local peer ID.
- Distinguish an authenticated connection from
PeerReady(Identify). - Confirm both the Cargo feature and its builder method.
- Match the capability’s
EndpointEventvariant (Nat,Gossipsub,Discovery,RelayServer,MdnsFailed) fromwaitorpoll, and check durable state with the snapshot getters (path,reachability,active_reservation,known_peers).
For the complete capability map, see the feature matrix.