astrid-runtime/astrid · error
MCP gateway control frame is missing or too large
Error message
MCP gateway control frame is missing or too large
What it means
read_bounded_line reads the control-frame bytes until a newline or its size bound; if neither the terminating \n nor the bound is reached (stream ended early or frame exceeded the limit without delimiter), it bails with this message.
Solutions
- Restart the gateway and retry the control operation — the stream likely died mid-frame
- Check gateway logs for a crash or panic around the request time
- Confirm nothing else is writing to the gateway socket (stale listener, another process)
- If frames legitimately grow, raise the frame size bound in the client and gateway together
Example fix
// before: single attempt
let ack = request_gateway_control(...).await?;
// after: retry once on truncated frame
let ack = match request_gateway_control(...).await {
Err(e) if e.to_string().contains("missing or too large") => {
restart_gateway().await?;
request_gateway_control(...).await?
},
other => other?,
}; Defensive patterns
Strategy: retry
Validate before calling
// verify the socket answers before sending control frames let outcome = connect_outcome(&gateway_socket_path()?).await?; anyhow::ensure!(!matches!(outcome, ConnectOutcome::Absent), "gateway socket absent");
Try / catch
match request_gateway_control(...).await {
Err(e) if e.to_string().contains("missing or too large") => {
restart_gateway().await?;
request_gateway_control(...).await?
},
other => other?,
} Prevention
- Check gateway logs for crashes producing truncated frames
- Don't point the client at a socket written by a different process
- Keep frame-size bounds identical on client and gateway
When it happens
Trigger: The gateway closes the control connection before sending a newline, or sends a frame larger than the bounded limit without a delimiter, while request_gateway_control reads the ack.
Common situations: Gateway crashed mid-response; a non-gateway process or error text wrote to the socket; a protocol mismatch producing an oversized undelimited blob.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- daemon closed the response stream before the final marker
- daemon rejected status request
- daemon returned an unexpected status response
- detached FUSE service exceeded the startup response size
- FSKit callback probe correlation mismatch
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/7b7f29bbdff84d3f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-cli/src/commands/mcp/lifecycle.rs:705
Ok(ack)
}
pub(crate) async fn read_bounded_line<R>(reader: &mut R) -> Result<Vec<u8>>
where
R: tokio::io::AsyncRead + Unpin,
{
let mut line = Vec::new();
for _ in 0..=MAX_CONTROL_BYTES {
let byte = reader
.read_u8()
.await
.context("failed to read control frame")?;
if byte == b'\n' {
return Ok(line);
}
line.push(byte);
}
anyhow::bail!("MCP gateway control frame is missing or too large")
}
async fn remove_dead_gateway_markers(record: &GatewayReady) -> Result<()> {
if crate::commands::daemon_control::is_process_alive(record.pid) {
anyhow::bail!(
"shutdown stage gateway.process_reap: PID {} is still alive",
record.pid
);
}
let lifecycle = try_acquire_gateway_lifecycle()?;
let Some(lifecycle) = lifecycle else {
anyhow::bail!(
"shutdown stage gateway.lifecycle_fence: a successor gateway lifecycle remains active"
);
};
let socket = gateway_socket_path()?;
match astrid_core::local_transport::connect_outcome(&socket)
.awaitView on GitHub (pinned to affd8760f4)