diem/diem · warning · StreamError
Client#{0} is closed
Error message
Client#{0} is closed What it means
`StreamError::ClientAlreadyClosed(u64)` indicates an operation was attempted on a streaming-RPC client identified by the given id, but that client's stream is already closed. Once a client disconnects or its stream is torn down, further sends/operations on it are rejected with this error.
Source
Thrown at json-rpc/src/stream_rpc/errors.rs:10
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0
use thiserror::Error;
#[derive(Debug, Error)]
pub enum StreamError {
#[error("Could not convert {0} to string")]
CouldNotStringifyMessage(String),
#[error("Client#{0} is closed")]
ClientAlreadyClosed(u64),
#[error("Received disconnect request message from client")]
ClientWantsToDisconnect,
#[error("Transport error: {0}")]
TransportError(String),
}
View on GitHub (pinned to fc4714a8ea)
Solutions
- Treat it as benign: check client liveness before sending and skip/log when already closed.
- Track client connection state in a registry and remove closed clients from notification fan-out lists.
- Synchronize close/send paths (e.g., use a single owner task per client) to avoid races between closing and sending.
- Ensure clients reconnect and re-subscribe; the id in the message identifies the stale client.
Defensive patterns
Strategy: try-catch
Type guard
fn is_client_closed(err: &StreamError, client_id: u64) -> bool {
matches!(err, StreamError::ClientAlreadyClosed(id) if *id == client_id)
} Try / catch
match notify_client(client_id, &msg) {
Err(StreamError::ClientAlreadyClosed(id)) => {
debug!("client#{} already closed; dropping notification", id);
registry.remove(id);
}
Err(e) => error!("notify failed: {}", e),
Ok(_) => {}
} Prevention
- Maintain a client registry and remove entries as soon as streams close.
- Route all sends through a per-client owner task so close/send races are eliminated.
- Treat sends to closed clients as benign and never retry them.
When it happens
Trigger: Sending a message or notification to a subscribed client after it disconnected or its channel was dropped/closed; racing a close with an in-flight send on the stream-rpc layer.
Common situations: Clients dropping WebSocket connections mid-subscription; server-side timeouts closing streams while notifications are still queued; double-close attempts during shutdown.
Related errors
- Transport error: {0}
- Could not convert {0} to string
- Received disconnect request message from client
- [json-rpc] failed to create runtime
- Specified output file path is a directory
AI-assisted analysis of diem/diem@fc4714a8ea (2026-09-04).
Data as JSON: /api/errors/b9aeb472f9de38a9.
Report an issue: GitHub.