Summary
In starknet-rust-tokio-tungstenite, inbound subscription notifications that fail to deserialize into StreamUpdateOrResponse are silently discarded.
This is a serious DX issue: subscription.recv() never yields, never errors, and no log is emitted, so callers see a healthy WebSocket that appears to hang indefinitely.
Repro context
- Crate rev:
7caedfe
- Server: pathfinder, JSON-RPC 0.8.x
- Call:
stream.subscribe_new_heads(ConfirmedBlockId::Latest)
- Handshake succeeds and subscribe ack is received
- Server sends
starknet_subscriptionNewHeads notifications
- Client reads frames from the socket, but
subscription.recv() never yields
Root cause
In [stream/read.rs](https://github.com/software-mansion/starknet-rust/blob/7caedfe/starknet-rust-tokio-tungstenite/src/stream/read.rs), handle_message returns HandleMessageResult::MalformedMessage when this fails:
serde_json::from_str::<StreamUpdateOrResponse>(text)
The run loop then drops it silently:
HandleMessageResult::Success | HandleMessageResult::MalformedMessage => {}
In our case this was caused by a spec mismatch from hitting the wrong RPC endpoint, but the silent drop made it very difficult to diagnose.
Suggested fix
At minimum, log the deserialization error and a truncated copy of the raw frame:
log::warn!("malformed websocket message: {err}; raw={truncated_text}");
Optionally, propagate this through Subscription::recv() as a non-fatal warning/error so callers can decide whether to reconnect.
This would make client/server spec drift visible instead of presenting as an indefinite hang.
Summary
In
starknet-rust-tokio-tungstenite, inbound subscription notifications that fail to deserialize intoStreamUpdateOrResponseare silently discarded.This is a serious DX issue:
subscription.recv()never yields, never errors, and no log is emitted, so callers see a healthy WebSocket that appears to hang indefinitely.Repro context
7caedfestream.subscribe_new_heads(ConfirmedBlockId::Latest)starknet_subscriptionNewHeadsnotificationssubscription.recv()never yieldsRoot cause
In
[stream/read.rs](https://github.com/software-mansion/starknet-rust/blob/7caedfe/starknet-rust-tokio-tungstenite/src/stream/read.rs),handle_messagereturnsHandleMessageResult::MalformedMessagewhen this fails:The
runloop then drops it silently:In our case this was caused by a spec mismatch from hitting the wrong RPC endpoint, but the silent drop made it very difficult to diagnose.
Suggested fix
At minimum, log the deserialization error and a truncated copy of the raw frame:
Optionally, propagate this through
Subscription::recv()as a non-fatal warning/error so callers can decide whether to reconnect.This would make client/server spec drift visible instead of presenting as an indefinite hang.