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

  1. Fix the stream assembler: split the buffer exactly on the totalLength read from each prelude, consuming frames sequentially with no gaps or overlaps.
  2. Retry the request — corruption usually originates from the upstream connection, not your code.
  3. Bypass any MITM/transforming proxy for the Kiro endpoint to rule out body rewriting.
  4. Log the expected vs actual byteLength and the prelude totalLength to identify whether your slicing logic or the network corrupted the frame.
  5. 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

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


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/c007ebd0a5cbb44a. Report an issue: GitHub.