diem/diem · error · NoiseHandshakeError

noise client: error finalizing secure connection: {0}

Error message

noise client: error finalizing secure connection: {0}

What it means

NoiseHandshakeError::ClientFinalizeFailed wraps a diem_crypto NoiseError raised when the client cannot finalize the secure connection after receiving the server's response — i.e. the second Noise handshake step fails cryptographically. This usually means the peer is not who the client expected (wrong static key) or the response bytes are corrupt/malicious.

Source

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

    #[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}")]
    InvalidClientPeerId(String),

    #[error("noise server: detected self-dial: we're trying to connect to ourselves")]
    SelfDialDetected,

    #[error(
        "noise server: client {0}: client is expecting us to have a different \
         public key: {1}"
    )]
    ClientExpectingDifferentPubkey(ShortHexStr, String),

    #[error("noise server: client {0}: error parsing handshake init message: {1}")]

View on GitHub (pinned to fc4714a8ea)

Solutions

  1. Verify the client's configured server public key matches the server's current static key.
  2. Ensure both sides use the identical Noise protocol name/handshake pattern and cipher suite.
  3. Capture and log the wrapped NoiseError ({0}) to identify MAC vs malformed-message failure.
  4. If keys rotated, update client config and redial; treat repeated failures as a possible security event.

Example fix

// before
trusted_pubkey = old_server_key; // rotated
// after
trusted_pubkey = fetch_current_server_key(peer_id); // update after rotation
Defensive patterns

Strategy: try-catch

Validate before calling

fn validate_pinned_key(expected: &X25519PublicKey, current: &X25519PublicKey) -> bool {
    expected == current
}

Try / catch

match handshake().await {
    Err(NoiseHandshakeError::ClientFinalizeFailed(e)) => {
        error!("could not authenticate server: {} — possible key rotation or MITM; do not retry blindly", e);
        alert_security_and_refresh_pinned_key();
    }
    ok => ok.map(|_| ())?,
}

Prevention

When it happens

Trigger: Calling the final handshake state transition (e.g. read_message/into_transport) on the client after the server's response arrives, when MAC verification fails, the remote static key doesn't match the expected server pubkey, or the message is malformed.

Common situations: Server rotated keys but client config still has the old pubkey; MITM or corrupted traffic; mismatched noise protocol name/pattern between endpoints; bug supplying wrong handshake state.

Related errors


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