diem/diem · error · NoiseHandshakeError

noise client: error sending client handshake init message: {

Error message

noise client: error sending client handshake init message: {0}

What it means

NoiseHandshakeError::ClientWriteFailed wraps an io::Error that occurred while the Noise client was sending its initial handshake message over the socket. It means the write itself failed — the connection could not carry the handshake bytes — rather than a crypto or rejection issue.

Source

Thrown at network/src/noise/error.rs:19

// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use diem_crypto::noise::NoiseError;
use diem_types::PeerId;
use short_hex_str::ShortHexStr;
use std::io;
use thiserror::Error;

/// Different errors than can be raised when negotiating a Noise handshake.
#[derive(Debug, Error)]
pub enum NoiseHandshakeError {
    #[error("noise client: MUST_FIX: missing remote server's public key when dialing")]
    MissingServerPublicKey,

    #[error("noise client: MUST_FIX: error building handshake init message: {0}")]
    BuildClientHandshakeMessageFailed(NoiseError),

    #[error("noise client: error sending client handshake init message: {0}")]
    ClientWriteFailed(io::Error),

    #[error(
        "noise client: error reading server handshake response message, server \
         probably rejected our handshake message: {0}"
    )]
    ClientReadFailed(io::Error),

    #[error("noise client: error flushing socket after writing: {0}")]
    ClientFlushFailed(io::Error),

    #[error("noise client: error finalizing secure connection: {0}")]
    ClientFinalizeFailed(NoiseError),

    #[error("noise server: error reading client handshake init message: {0}")]
    ServerReadFailed(io::Error),

    #[error("noise server: client peer id is malformed: {0}")]

View on GitHub (pinned to fc4714a8ea)

Solutions

  1. Check the server is running and reachable at the dialed address.
  2. Retry the dial with backoff — transient network failures commonly surface here.
  3. Inspect the wrapped io::Error for ECONNRESET/EPIPE and verify firewall/NAT rules between client and server.
  4. Verify the server's connection limits (max inbound peers) are not causing immediate disconnects.
Defensive patterns

Strategy: retry

Validate before calling

async fn can_reach(addr: SocketAddr) -> bool {
    tokio::net::TcpStream::connect(addr).await.is_ok()
}

Try / catch

match dial().await {
    Err(NoiseHandshakeError::ClientWriteFailed(e)) if is_transient(&e) => {
        backoff::retry(dial, MAX_ATTEMPTS).await
    }
    Err(e @ NoiseHandshakeError::ClientWriteFailed(_)) => { error!("fatal write failure: {}", e); Err(e) }
    ok => ok.map(|_| ())?,
}

fn is_transient(e: &io::Error) -> bool {
    matches!(e.kind(), io::ErrorKind::ConnectionReset | io::ErrorKind::ConnectionAborted | io::ErrorKind::TimedOut)
}

Prevention

When it happens

Trigger: Calling write/send on the TCP (or other transport) stream during the first phase of the Noise client handshake when the socket is closed, reset, or otherwise unreadable by the OS.

Common situations: Server not listening or crashed mid-handshake; connection reset by peer/firewall; TLS or proxy layer terminated; network partition right after connect.

Understand the failure class

Related errors


AI-assisted analysis of diem/diem@fc4714a8ea (2026-09-04). Data as JSON: /api/errors/25e0abd68715fc81. Report an issue: GitHub.