openai/codex · error · io::Error
InvalidData
InvalidData
Error message
oversized MCP SSE event was already rejected
What it means
SseEventSizeLimit::observe() is sticky: once an SSE event breached the per-event size limit, the failed flag stays set and every subsequent body chunk observation returns this InvalidData error. It exists so the byte stream built in sse_stream_from_body keeps erroring instead of silently resuming mid-event after the real failure. Seeing it means the stream already produced the primary 'MCP response body exceeds N bytes' error earlier.
Source
Thrown at codex-rs/rmcp-client/src/http_client_adapter.rs:999
previous_was_carriage_return: bool,
failed: bool,
}
impl SseEventSizeLimit {
fn new(maximum_bytes: Option<usize>) -> Self {
Self {
maximum_bytes,
retained_bytes: 0,
line_bytes: 0,
line_is_comment: false,
previous_was_carriage_return: false,
failed: false,
}
}
fn observe(&mut self, bytes: &[u8]) -> io::Result<()> {
if self.failed {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"oversized MCP SSE event was already rejected",
));
}
let Some(maximum_bytes) = self.maximum_bytes else {
return Ok(());
};
for &byte in bytes {
if self.previous_was_carriage_return {
self.previous_was_carriage_return = false;
if byte == b'\n' {
continue;
}
}
match byte {
b'\r' => {View on GitHub (pinned to 339751715c)
Solutions
- Treat any error from the SSE stream as terminal: stop polling, drop the connection, reconnect
- Find the first 'MCP response body exceeds N bytes' error earlier in the same stream/log — that is the root cause
- Fix the server so no single event exceeds the limit
- If you aggregate errors, deduplicate consecutive stream errors to avoid alarm noise
Example fix
// before
while let Some(item) = event_stream.next().await { /* logs a cascade of 'already rejected' */ }
// after
while let Some(item) = event_stream.next().await {
if item.is_err() { break; } // first error is fatal; reconnect
} Defensive patterns
Strategy: try-catch
Type guard
fn is_already_rejected(error: &std::io::Error) -> bool {
error.kind() == std::io::ErrorKind::InvalidData
&& error.to_string().contains("already rejected")
} Try / catch
// sticky failure: first error (any error) is terminal for the stream
while let Some(item) = event_stream.next().await {
let item = match item {
Ok(item) => item,
Err(error) => {
warn!("SSE stream failed ({error}); dropping connection");
break; // reconnect rather than keep polling
}
};
} Prevention
- Treat every SSE stream error as fatal to that connection
- When debugging, scroll to the FIRST size error — 'already rejected' is only an echo
- Deduplicate consecutive stream errors in log aggregation
When it happens
Trigger: Any bytes arriving on the HTTP body stream after an earlier check_limit() failure — e.g. the caller keeps polling the SSE stream after the first oversize error and the server keeps sending the remainder of the huge event.
Common situations: Drain loops that log every SSE item and show a cascade of these errors; retry logic polling a dead stream; reading logs where the first real error scrolled past and only these remain visible.
Related errors
- invalid requirement for MCP server `{server_name}` (set by {
- InvalidData
- generated image exceeds the executor file size limit
- failed to read MCP config for selected plugin `{plugin_id}`
- failed to resolve MCP config path `{relative_path}` below se
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/3ce88148d15d2127.
Report an issue: GitHub.