xai-org/grok-build · error · io::Error (InvalidData)

ACP message exceeds {} byte limit ({} bytes read)

Error message

ACP message exceeds {} byte limit ({} bytes read)

What it means

Thrown by `read_line_capped` in the ACP (Agent Client Protocol) line reader when a single newline-delimited message exceeds MAX_LINE_SIZE bytes. Because ACP frames are length-unbounded JSON lines, this guard prevents unbounded memory growth from a malicious or buggy peer. The reader consumes the bytes, sees buf.len() > MAX_LINE_SIZE, and returns InvalidData before any parsing occurs.

Source

Thrown at crates/codegen/xai-acp-lib/src/line_reader.rs:164

        let (consumed, done) = {
            let available = reader.fill_buf().await?;
            if available.is_empty() {
                return Ok(buf.len()); // EOF
            }
            match available.iter().position(|&b| b == b'\n') {
                Some(pos) => {
                    buf.extend_from_slice(&available[..=pos]);
                    (pos + 1, true)
                }
                None => {
                    buf.extend_from_slice(available);
                    (available.len(), false)
                }
            }
        };
        reader.consume_unpin(consumed);
        if buf.len() > MAX_LINE_SIZE {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    "ACP message exceeds {} byte limit ({} bytes read)",
                    MAX_LINE_SIZE,
                    buf.len()
                ),
            ));
        }
        if done {
            return Ok(buf.len());
        }
    }
}

#[cfg(test)]
mod tests {
    use futures::{AsyncReadExt as _, io::Cursor};

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Truncate or paginate large payloads on the sending side (chunk tool outputs, use file references) so no single ACP line exceeds the limit.
  2. Verify the peer actually terminates every JSON message with a newline; fix any writer that omits it.
  3. If legitimately large messages are expected, raise MAX_LINE_SIZE in xai-acp-lib to a safe upper bound.
  4. Confirm the transport carries text ACP frames, not binary/interleaved output from another process.

Example fix

// before (sender, agent side)
let msg = serde_json::to_vec(&huge_result)?;
stream.write_all(&msg).await?;
// after
let msg = serde_json::to_vec(&truncate_result(&huge_result, MAX_INLINE_BYTES))?;
stream.write_all(&msg).await?;
stream.write_all(b"\n").await?;
Defensive patterns

Strategy: type-guard

Type guard

fn is_oversized_acp_message(err: &io::Error) -> bool {
    err.kind() == io::ErrorKind::InvalidData
        && err.to_string().contains("ACP message exceeds")
}

Try / catch

match reader.read_line_capped(&mut buf).await {
    Ok(n) => parse(&buf[..n])?,
    Err(e) if is_oversized_acp_message(&e) => {
        tracing::warn!("peer sent oversized ACP line; dropping frame");
        // resync reader on next newline
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Reading from an ACP stream whose peer emits a line longer than MAX_LINE_SIZE — an oversized JSON-RPC notification/result (e.g. a huge tool output inlined in a message), a missing newline causing frames to merge, or a non-ACP peer writing arbitrary data to the pipe.

Common situations: Agent returning enormous file contents or diffs inline in one message; protocol desync where a payload without a trailing newline swallows subsequent frames; connecting the reader to a stream that emits binary data.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/86fae5423064a6ff. Report an issue: GitHub.