libp2p / libp2p/unified-testing
c-v0.0.1 x jvm-v1.2 yamux ping stream premature close
Nobody has claimed this yet.
- Dominant language
- Shell
- Stars
- 7
- Forks
- 19
- PR merge metrics
- No merged PRs in 30d
Description
**Repos:**
- [Pier-Two/c-libp2p](https://github.com/Pier-Two/c-libp2p) (commit `23a617223a3bbfb4b2af8f219f389e440b9c1ac2`)
- [libp2p/jvm-libp2p](https://github.com/libp2p/jvm-libp2p) (commit `737456de5d96db030aa04c8527585af1a510898f`)
**Transport:** tcp, **Secure:** noise, **Muxer:** yamux
## Summary
When c-v0.0.1 dials jvm-v1.2 via tcp/noise/yamux, the connection and protocol negotiation succeed, but the ping fails. The JVM listener sends a FIN (flags=0x4) on the ping stream before the c dialer can complete the ping exchange, causing the c dialer's stream read to fail with error -7.
## Failing tests (1)
- `c-v0.0.1 x jvm-v1.2 (tcp, noise, yamux)`
## Error output
**c dialer:**
```
[YAMUX] read frame type=0 id=3 flags=0x4 len=0 <-- JVM closes stream 3 (FIN)
[YAMUX_PROCESS_ONE] dispatch rc=-4 type=0 id=3 flags=0x4 len=0 stop=1 goaway=0
[YAMUX_STREAM_RECV] process_one returned -4 for id=1 (fatal)
[PING] stream_read_exact error n=-7
[ERROR] ping failed (err=-3)
```
## Timeline
1. TCP connection established, noise handshake completes
2. Yamux session negotiated (stream 1 for multistream-select, stream 3 for ping)
3. Multistream-select succeeds on stream 1 (identify) and stream 3 (ping protocol)
4. c dialer sends 32-byte ping payload on stream 1
5. JVM listener sends FIN on stream 3 (flags=0x4) — **premature close**
6. c dialer receives the FIN, yamux marks the session as stopped
7. Ping read fails with error -7 (stream closed)
8. c dialer exits with code 1
## Root cause analysis
The JVM listener closes the yamux ping stream (stream 3) with a FIN frame before the ping echo response is sent back. The c dialer's yamux implementation treats receiving a FIN on any stream as a fatal event for the session (`rc=-4`, `stop=1`), which prevents reading the ping response on stream 1.
Two issues contribute:
1. **JVM side:** jvm-libp2p closes the ping stream prematurely, possibly because its ping handler completes and closes the stream before the echo response is fully transmitted
2. **c side:** c-libp2p's yamux implementation treats a FIN on one stream as fatal for the entire session, rather than just that individual stream
## Notes
- The reverse direction (`jvm-v1.2 x c-v0.0.1 tcp/noise/yamux`) shows a different failure pattern — it falls into the "listener exits first" category where the ping actually succeeds but the JVM dialer is killed.
- Both implementations work with other peers via tcp/noise/yamux, so this is specific to the c/jvm combination.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the failing `c-v0.0.1 x jvm-v1.2 (tcp, noise, yamux)` interoperability test and reviewing the referenced c-libp2p and jvm-libp2p commits. Trace the ping stream and yamux FIN handling on both sides to determine which implementation closes or treats the stream incorrectly. Done means the ping exchange completes in this test without the premature-close failure.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- distributed-systems, networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100