vllm-project/vllm · error · Error
unexpected engine id in startup handshake: expected {expecte
Error message
unexpected engine id in startup handshake: expected {expected:?}, got {actual:?} What it means
Error::UnexpectedHandshakeIdentity carries expected and actual engine id bytes and is raised by decode_handshake_message (transport.rs:420-427) when a handshake frame arrives with a ZMQ routing identity different from the one expected in contexts where the peer identity is already pinned (expected_id is Some).
Source
Thrown at rust/src/engine-core-client/src/error.rs:48
ExtValueDecode { message: String },
#[error("invalid structured outputs params: {message}")]
InvalidStructuredOutputsParams { message: String },
#[error("io error")]
Io(#[from] std::io::Error),
#[error("transport error")]
Transport(#[from] zeromq::ZmqError),
#[error("ZMQ runtime task failed")]
ZmqRuntimeTask(#[from] tokio::task::JoinError),
#[error("engine core reported fatal failure")]
EngineCoreDead,
#[error("startup handshake timed out while waiting for {stage} after {timeout:?}")]
HandshakeTimeout {
stage: &'static str,
timeout: Duration,
},
#[error("engine input registration timed out after {timeout:?}")]
InputRegistrationTimeout { timeout: Duration },
#[error("unexpected engine id in startup handshake: expected {expected:?}, got {actual:?}")]
UnexpectedHandshakeIdentity { expected: Vec<u8>, actual: Vec<u8> },
#[error("unexpected startup handshake message: {message}")]
UnexpectedHandshakeMessage { message: String },
#[error("unexpected non-control output on coordinator path: {message}")]
UnexpectedCoordinatorOutput { message: String },
#[error("unexpected output on main dispatcher path: {message}")]
UnexpectedDispatcherOutput { message: String },
#[error("coordinator requires a Python-compatible two-byte engine id, got {engine_id:?}")]
UnsupportedCoordinatorEngineId { engine_id: Vec<u8> },
#[error("unsupported auxiliary frame(s): expected 1 frame, got {frame_count}")]
UnsupportedAuxFrames { frame_count: usize },
#[error("external coordinator mode is not implemented yet")]
UnsupportedExternalCoordinator,
#[error("unsupported field `{field}` in {context}")]
UnsupportedField {
context: &'static str,
field: &'static str,
},View on GitHub (pinned to c794754062)
Solutions
- Compare expected vs actual byte arrays: identical ids from two sources = duplicate engine id configuration
- Ensure each engine derives a unique identity (engine index/rank) and no ids collide across the fleet
- Tear down stale sockets/processes from previous runs before reconnecting
- If an engine legitimately restarted, restart the frontend client too so identity expectations reset
Defensive patterns
Strategy: validation
Validate before calling
fn unique_engine_ids(ids: &[Vec<u8>]) -> bool {
let mut seen = std::collections::HashSet::new();
ids.iter().all(|id| seen.insert(id.clone()))
} Type guard
fn is_identity_mismatch(e: &engine_core_client::Error) -> bool {
matches!(e, engine_core_client::Error::UnexpectedHandshakeIdentity { .. })
} Try / catch
if let engine_core_client::Error::UnexpectedHandshakeIdentity { expected, actual } = &err {
tracing::error!(?expected, ?actual, "engine identity collision; restart frontend with clean identity set");
} Prevention
- Derive every engine id from its unique rank/index; never hand-assign overlapping ids
- Fully tear down sockets and processes from previous cluster runs before reconnecting
- Treat identity mismatch as fatal for the connect attempt; restart both sides
When it happens
Trigger: Receiving a handshake message on a socket where the frontend already knows the engine's identity, but frames[0] (the ZMQ identity frame) does not match — e.g. a duplicate or misconfigured engine reusing another engine's socket, or an engine restarted with a fresh identity while stale registrations are still in flight.
Common situations: Two engines configured with the same engine id (copy-paste of worker configs), a restarted engine whose identity changed while the frontend still expects the old one, or leftover sockets from a previous cluster run still sending frames. The expected/got byte arrays in the message directly show the collision.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- startup handshake timed out while waiting for {stage} after
- coordinator requires a Python-compatible two-byte engine id,
- managed frontend engine count ({engine_count}) must equal da
- {kind} parser `{name}` is not registered{}
- gpt_oss uses native Harmony output parsing; generic {kind} p
AI-assisted analysis of vllm-project/vllm@c794754062 (2026-08-14).
Data as JSON: /api/errors/91742d9c410a89ca.
Report an issue: GitHub.