{"record":{"id":"beb60a2d2d8431ac","repo":"decolua/9router","slug":"aws-eventstream-frame-bounds-are-invalid","errorCode":null,"errorMessage":"AWS EventStream frame bounds are invalid","messagePattern":"AWS EventStream frame bounds are invalid","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/executors/kiro.js","lineNumber":1209,"sourceCode":"\n/**\n * Parse AWS EventStream frame\n */\n\nfunction parseEventFrame(data) {\n  if (!(data instanceof Uint8Array) || data.byteLength < 16) {\n    throw new Error(\"AWS EventStream frame is shorter than 16 bytes\");\n  }\n  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n  const totalLength = view.getUint32(0, false);\n  const headersLength = view.getUint32(4, false);\n  if (totalLength !== data.byteLength) {\n    throw new Error(\"AWS EventStream frame length does not match its prelude\");\n  }\n  if (totalLength > EVENTSTREAM_MAX_MESSAGE_BYTES ||\n      headersLength > EVENTSTREAM_MAX_HEADERS_BYTES ||\n      headersLength > totalLength - 16) {\n    throw new Error(\"AWS EventStream frame bounds are invalid\");\n  }\n  if (view.getUint32(8, false) !== crc32(data.subarray(0, 8))) {\n    throw new Error(\"AWS EventStream prelude CRC mismatch\");\n  }\n  if (view.getUint32(totalLength - 4, false) !== crc32(data.subarray(0, totalLength - 4))) {\n    throw new Error(\"AWS EventStream message CRC mismatch\");\n  }\n\n  const headers = Object.create(null);\n  const names = new Set();\n  let offset = 12;\n  const headerEnd = offset + headersLength;\n  const requireBytes = (count) => {\n    if (offset + count > headerEnd) {\n      throw new Error(\"AWS EventStream header exceeds its declared bounds\");\n    }\n  };\n","sourceCodeStart":1191,"sourceCodeEnd":1227,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/executors/kiro.js#L1191-L1227","documentation":"After the prelude passes basic consistency checks, parseEventFrame() sanity-checks the declared totalLength against EVENTSTREAM_MAX_MESSAGE_BYTES, the headersLength against EVENTSTREAM_MAX_HEADERS_BYTES, and requires headersLength to fit within totalLength - 16 (prelude + CRC). This is a resource-exhaustion / malicious-payload guard: an absurd length would otherwise drive huge allocations or out-of-bounds header parsing.","triggerScenarios":"A corrupted or hostile frame declares a totalLength or headersLength beyond configured maxima, or headersLength so large it cannot fit inside the frame (headersLength > totalLength - 16) — e.g. random bytes interpreted as a prelude, or a length field flipped by transmission corruption.","commonSituations":"Connecting to a non-EventStream endpoint or a proxy error page whose bytes are misparsed as a prelude; a man-in-the-middle rewriting binary bodies; desynced stream parsing where a payload's bytes are consumed as a new frame header; memory-limit misconfiguration making legit huge frames exceed EVENTSTREAM_MAX_MESSAGE_BYTES.","solutions":["Re-sync stream parsing: after each frame, advance exactly totalLength bytes so payload bytes are never misread as the next prelude.","Retry the request — random corruption points to the transport, not the frame format.","Bypass MITM/transforming proxies (or TLS-inspecting firewalls) for the Kiro endpoint.","If legitimate frames exceed the caps, raise EVENTSTREAM_MAX_MESSAGE_BYTES / EVENTSTREAM_MAX_HEADERS_BYTES in open-sse config for very large tool_result payloads.","Verify the Kiro base URL points at the real CodeWhisperer/Kiro EventStream endpoint, not an HTML error page."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function frameBoundsOk(data, MAX_MSG, MAX_HDR) {\n  if (!(data instanceof Uint8Array) || data.byteLength < 16) return false;\n  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n  const total = view.getUint32(0, false), hdr = view.getUint32(4, false);\n  return total === data.byteLength && total <= MAX_MSG && hdr <= MAX_HDR && hdr <= total - 16;\n}\nif (frameBoundsOk(buf, EVENTSTREAM_MAX_MESSAGE_BYTES, EVENTSTREAM_MAX_HEADERS_BYTES)) parseEventFrame(buf);","typeGuard":"function hasSanePrelude(v) {\n  if (!(v instanceof Uint8Array) || v.byteLength < 16) return false;\n  const view = new DataView(v.buffer, v.byteOffset, v.byteLength);\n  const total = view.getUint32(0, false), hdr = view.getUint32(4, false);\n  return total <= 0x4000000 && hdr <= total - 16;\n}","tryCatchPattern":"try {\n  parseEventFrame(frame);\n} catch (e) {\n  if (e.message.includes('bounds are invalid')) {\n    log.error('kiro: insane frame bounds, possible desync or MITM');\n    cancelAndResyncStream();\n  } else throw e;\n}","preventionTips":["Verify the endpoint really speaks EventStream — parse only bodies with content-type application/vnd.amazon.eventstream or similar.","Never feed non-stream bytes (HTML error pages, JSON error bodies) into the binary parser; check response status/content-type first.","Keep prelude caps (EVENTSTREAM_MAX_MESSAGE_BYTES/HEADERS_BYTES) in sync with real upstream frame sizes.","After a bounds failure, assume stream desync and resynchronize rather than continuing to parse."],"tags":["eventstream","binary-protocol","bounds-check","kiro","security"],"backgroundTag":"eventstream-invalid-frame-bounds","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}