decolua/9router · error
AWS EventStream message CRC mismatch
Error message
AWS EventStream message CRC mismatch
What it means
Every EventStream frame ends with a CRC32 of the entire message (all bytes except the trailing CRC itself). parseEventFrame() computes crc32(data.subarray(0, totalLength - 4)) and compares it to the last 4 bytes; mismatch means the frame's payload/headers were corrupted after the prelude CRC passed. This is the final integrity gate before headers and JSON payload are decoded.
Source
Thrown at open-sse/executors/kiro.js:1215
if (!(data instanceof Uint8Array) || data.byteLength < 16) {
throw new Error("AWS EventStream frame is shorter than 16 bytes");
}
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
const totalLength = view.getUint32(0, false);
const headersLength = view.getUint32(4, false);
if (totalLength !== data.byteLength) {
throw new Error("AWS EventStream frame length does not match its prelude");
}
if (totalLength > EVENTSTREAM_MAX_MESSAGE_BYTES ||
headersLength > EVENTSTREAM_MAX_HEADERS_BYTES ||
headersLength > totalLength - 16) {
throw new Error("AWS EventStream frame bounds are invalid");
}
if (view.getUint32(8, false) !== crc32(data.subarray(0, 8))) {
throw new Error("AWS EventStream prelude CRC mismatch");
}
if (view.getUint32(totalLength - 4, false) !== crc32(data.subarray(0, totalLength - 4))) {
throw new Error("AWS EventStream message CRC mismatch");
}
const headers = Object.create(null);
const names = new Set();
let offset = 12;
const headerEnd = offset + headersLength;
const requireBytes = (count) => {
if (offset + count > headerEnd) {
throw new Error("AWS EventStream header exceeds its declared bounds");
}
};
while (offset < headerEnd) {
requireBytes(1);
const nameLength = data[offset++];
requireBytes(nameLength + 1);
const name = decoder.decode(data.subarray(offset, offset + nameLength));
offset += nameLength;View on GitHub (pinned to 90b52e06ff)
Solutions
- Copy each complete frame out of the read buffer (slice, not subarray) before parsing so later reads cannot mutate it.
- Retry the request — corruption is usually transient transport damage.
- Bypass transforming proxies (compression/rewriting layers) for the Kiro EventStream endpoint.
- Drop and resynchronize the stream after a bad frame instead of continuing (the executor already cancels the reader on invalid frames — keep that behavior).
- Update Node/undici/9router if the corruption reproduces on every request (possible SDK streaming bug).
Example fix
// before: parse deferred while buffer stays shared with the reader pendingFrames.push(chunk.subarray(start, end)); // ...later, after more reads... parseEventFrame(pendingFrames[0]); // bytes may already be overwritten // after: parse or copy immediately const frame = chunk.slice(start, end); parseEventFrame(frame);
Defensive patterns
Strategy: try-catch
Try / catch
try {
const { headers, payload } = parseEventFrame(frame);
handleFrame(headers, payload);
} catch (e) {
if (e.message.includes('message CRC mismatch')) {
log.warn('kiro: frame payload corrupted, aborting stream');
await reader.cancel('invalid Kiro EventStream');
await retryWithBackoff();
} else throw e;
} Prevention
- Parse each complete frame immediately after assembly, or copy it — never let the reader reuse the underlying ArrayBuffer first.
- Disable body-transforming middlewares (compression, rewriting) on the EventStream path.
- On mismatch, cancel the upstream reader and retry — the executor's existing cancel-then-break pattern is correct.
- If every request fails message CRC, suspect the Node/undici streaming layer or a middlebox and test from another network.
When it happens
Trigger: The recomputed message CRC differs from view.getUint32(totalLength - 4, false): bytes between offset 12 and totalLength-4 were modified in transit, the buffer contents changed between read and parse (reused underlying ArrayBuffer), or the parser is reading a frame assembled from mis-joined chunks.
Common situations: Flaky upstream connection or corrupted intermediate hop; a shared/aliased buffer overwritten by a subsequent reader.read() before parsing; a proxy re-encoding (e.g. gzipping) the binary body; desynced multi-frame parsing where chunk boundaries were stitched incorrectly.
Related errors
- AWS EventStream prelude CRC mismatch
- AWS EventStream frame is shorter than 16 bytes
- AWS EventStream frame length does not match its prelude
- AWS EventStream frame bounds are invalid
- AWS EventStream header exceeds its declared bounds
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/0cc4f862008a50b7.
Report an issue: GitHub.