decolua/9router · error
AWS EventStream frame length does not match its prelude
Error message
AWS EventStream frame length does not match its prelude
What it means
AWS EventStream frames encode their own total byte length in the first 4 bytes (big-endian). parseEventFrame() throws this when the declared totalLength does not equal the actual byteLength of the buffer — the frame boundary computed from the prelude disagrees with the data received. This guards against mis-assembled frames and corrupted or forged framing bytes.
Source
Thrown at open-sse/executors/kiro.js:1204
log?.error?.("TOKEN", `Kiro refresh error: ${error.message}`);
return null;
}
}
}
/**
* Parse AWS EventStream frame
*/
function parseEventFrame(data) {
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) => {View on GitHub (pinned to 90b52e06ff)
Solutions
- Fix the stream assembler: split the buffer exactly on the totalLength read from each prelude, consuming frames sequentially with no gaps or overlaps.
- Retry the request — corruption usually originates from the upstream connection, not your code.
- Bypass any MITM/transforming proxy for the Kiro endpoint to rule out body rewriting.
- Log the expected vs actual byteLength and the prelude totalLength to identify whether your slicing logic or the network corrupted the frame.
- Update 9router if the Kiro upstream changed framing (e.g. new padding or compression).
Example fix
// before: advancing by data.length instead of the frame's declared length const frame = buf.subarray(0, 1024); parseEventFrame(frame); // after: slice on the prelude-declared totalLength const totalLength = new DataView(buf.buffer, buf.byteOffset).getUint32(0, false); if (buf.byteLength < totalLength) return; // wait for more bytes parseEventFrame(buf.subarray(0, totalLength)); buf = buf.subarray(totalLength);
Defensive patterns
Strategy: validation
Validate before calling
function frameLengthMatches(data) {
if (!(data instanceof Uint8Array) || data.byteLength < 16) return false;
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
return view.getUint32(0, false) === data.byteLength;
}
if (frameLengthMatches(buf)) parseEventFrame(buf); Type guard
function isWellFramedBuffer(v) {
return v instanceof Uint8Array && v.byteLength >= 16 &&
new DataView(v.buffer, v.byteOffset, v.byteLength).getUint32(0, false) === v.byteLength;
} Try / catch
try {
parseEventFrame(frame);
} catch (e) {
if (e.message.includes('does not match its prelude')) {
log.error('kiro: framing desync, expected', frame.byteLength, 'declared', declaredLength);
resetStreamAndRetry();
} else throw e;
} Prevention
- Advance the parse offset by exactly the prelude totalLength after each frame — never by chunk length.
- Wait for the full frame (totalLength bytes buffered) before calling parseEventFrame.
- Log declared vs actual length on failure to distinguish assembler bugs from network corruption.
- Keep a single well-tested frame-splitting routine instead of ad-hoc slicing at call sites.
When it happens
Trigger: A frame buffer is passed to parseEventFrame whose byte count differs from view.getUint32(0, false): the caller sliced/concated the stream buffer at the wrong offset, dropped or duplicated bytes, or the frame's length header itself was corrupted in transit.
Common situations: Manual chunk reassembly with an off-by-one/off-by-CRC (16-byte) error; a frame split across TCP/HTTP2 segments and re-joined incorrectly; a buggy custom proxy or HTTP interceptor rewriting the body; corrupted padding when a partial final frame is padded to a boundary.
Related errors
- AWS EventStream frame is shorter than 16 bytes
- AWS EventStream frame bounds are invalid
- AWS EventStream prelude CRC mismatch
- AWS EventStream message CRC mismatch
- AWS EventStream header exceeds its declared bounds
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/c007ebd0a5cbb44a.
Report an issue: GitHub.