astrid-runtime/astrid · error
Handshake response too large
Error message
Handshake response too large: {resp_len} bytes What it means
send_request_read_response reads the handshake response's 4-byte length prefix and rejects any declared size above MAX_HANDSHAKE_RESPONSE_SIZE before allocating and reading the payload. Handshake responses are small by protocol; an oversized prefix indicates corruption, desync, or a hostile peer, so it fails fast.
Solutions
- Reconnect and retry the handshake to restore a clean stream state.
- Verify the request written before the read was correctly framed (length-prefixed); a bad request can corrupt the response stream.
- Align client and daemon versions so both use the same handshake framing and MAX_HANDSHAKE_RESPONSE_SIZE.
- If responses legitimately grew beyond the cap, raise MAX_HANDSHAKE_RESPONSE_SIZE on both sides.
Example fix
// before: writing the request without its length prefix desyncs the response framing stream.write_all(&request_payload).await?; // after let mut framed = Vec::with_capacity(4 + request_payload.len()); framed.extend_from_slice(&(request_payload.len() as u32).to_be_bytes()); framed.extend_from_slice(&request_payload); stream.write_all(&framed).await?;
Defensive patterns
Strategy: try-catch
Validate before calling
// Sanity-bound the request you send; a corrupt request can corrupt the response stream
if request_payload.len() > MAX_HANDSHAKE_REQUEST_SIZE {
return Err(anyhow::anyhow!("handshake request too large"));
} Try / catch
match perform_handshake_in_home(&socket_path).await {
Ok(auth) => auth,
Err(e) if e.to_string().contains("Handshake response too large") => {
// framing desynced: reconnect and retry once with a clean stream
let mut client = reconnect(&socket_path).await?;
perform_handshake_on(&mut client).await
}
Err(e) => return Err(e),
} Prevention
- Always write length-prefixed requests so response framing stays aligned.
- Keep MAX_HANDSHAKE_RESPONSE_SIZE consistent on both ends of the protocol.
- Reconnect rather than continue reading once the stream is suspect.
- Add a protocol test asserting response sizes stay under the cap.
When it happens
Trigger: Calling send_request_read_response (via perform_handshake_in_home) when the response length prefix exceeds MAX_HANDSHAKE_RESPONSE_SIZE — e.g. the peer wrote raw payload without framing, the stream is misaligned after a failed write, or a garbage/broken response arrives.
Common situations: Handshake request was malformed so the daemon's error output (e.g. an HTML/log blob) is misread as a framed response; socket desynchronization from a previous oversized write; incompatible daemon version framing the response differently.
Understand the failure class
Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Message too large from kernel
- daemon closed the response stream before the final marker
- Daemon rejected connection
- daemon rejected status request
- daemon returned an unexpected status response
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/7281cfee7bc841a5.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-uplink/src/socket_client.rs:614
tokio::time::timeout(HANDSHAKE_TIMEOUT, async {
stream.write_all(&len.to_be_bytes()).await?;
stream.write_all(&request_bytes).await?;
stream.flush().await?;
Ok::<(), std::io::Error>(())
})
.await
.context("Handshake request write timed out")?
.context("Failed to send handshake request")?;
let mut len_buf = [0u8; 4];
tokio::time::timeout(HANDSHAKE_TIMEOUT, stream.read_exact(&mut len_buf))
.await
.context("Handshake response timed out")?
.context("Failed to read handshake response length")?;
let resp_len = u32::from_be_bytes(len_buf) as usize;
if resp_len > MAX_HANDSHAKE_RESPONSE_SIZE {
anyhow::bail!("Handshake response too large: {resp_len} bytes");
}
let mut resp_payload = vec![0u8; resp_len];
tokio::time::timeout(HANDSHAKE_TIMEOUT, stream.read_exact(&mut resp_payload))
.await
.context("Handshake response payload timed out")?
.context("Failed to read handshake response payload")?;
serde_json::from_slice(&resp_payload).context("Failed to parse handshake response")
}
View on GitHub (pinned to affd8760f4)