{"record":{"id":"0827a022cec06feb","repo":"decolua/9router","slug":"aws-eventstream-frame-is-shorter-than-16-bytes","errorCode":null,"errorMessage":"AWS EventStream frame is shorter than 16 bytes","messagePattern":"AWS EventStream frame is shorter than 16 bytes","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/executors/kiro.js","lineNumber":1198,"sourceCode":"        log,\n        proxyOptions\n      );\n\n      return result;\n    } catch (error) {\n      log?.error?.(\"TOKEN\", `Kiro refresh error: ${error.message}`);\n      return null;\n    }\n  }\n}\n\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  }","sourceCodeStart":1180,"sourceCodeEnd":1216,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/executors/kiro.js#L1180-L1216","documentation":"The Kiro executor's parseEventFrame() decodes AWS EventStream binary frames, which always begin with a 12-byte prelude (total length, headers length, CRC) plus a 4-byte message CRC — 16 bytes minimum. This error is thrown when the buffer handed to the parser is not a Uint8Array or contains fewer than 16 bytes, meaning it cannot possibly hold a complete frame. It is a fail-fast integrity check on the upstream Kiro (CodeWhisperer) EventStream transport.","triggerScenarios":"parseEventFrame() receives a Uint8Array with byteLength < 16, or a non-Uint8Array value, from the network chunk assembler feeding it — typically a partial/truncated frame fragment at the start or end of the HTTP/2 body stream from the Kiro upstream.","commonSituations":"Upstream connection cut mid-frame (proxy, network blip, idle timeout); a buffering bug where stream chunks are passed to the parser before being accumulated to full-frame boundaries; a MITM/intercepting proxy mangling the binary body; Kiro returning an error page or empty body instead of an EventStream.","solutions":["Re-stream/accumulate bytes and only hand complete frames to parseEventFrame (ensure the caller buffers partial chunks until totalLength bytes are available).","Retry the Kiro request — a truncated frame usually means the upstream connection was dropped mid-stream.","Check for a proxy (HTTP_PROXY/HTTPS_PROXY or corporate MITM) corrupting binary EventStream bodies and bypass it for the Kiro endpoint.","Verify the Kiro credentials/account are valid; an auth failure can produce a non-EventStream body that fragments into tiny chunks.","Update 9router — upstream protocol changes may require a newer executor/translator."],"exampleFix":"// before: parsing raw network chunks directly\nfor (const chunk of chunks) parseEventFrame(chunk);\n\n// after: buffer until a full frame is available\nlet buf = new Uint8Array(0);\nfor (const chunk of chunks) {\n  buf = concat(buf, chunk);\n  if (buf.byteLength >= 16) {\n    const totalLength = new DataView(buf.buffer).getUint32(0, false);\n    if (buf.byteLength >= totalLength) {\n      parseEventFrame(buf.subarray(0, totalLength));\n      buf = buf.subarray(totalLength);\n    }\n  }\n}","handlingStrategy":"validation","validationCode":"function isCompleteEventStreamFrame(data) {\n  return data instanceof Uint8Array &&\n    data.byteLength >= 16 &&\n    new DataView(data.buffer, data.byteOffset, data.byteLength).getUint32(0, false) === data.byteLength;\n}\nif (isCompleteEventStreamFrame(buf)) parseEventFrame(buf);","typeGuard":"function isFrameCandidate(v) {\n  return v instanceof Uint8Array && v.byteLength >= 16;\n}","tryCatchPattern":"try {\n  parseEventFrame(frame);\n} catch (e) {\n  if (e.message.includes('shorter than 16 bytes')) {\n    log.warn('kiro: truncated frame, reconnecting');\n    await retryRequest();\n  } else throw e;\n}","preventionTips":["Always accumulate stream chunks and only parse when byteLength >= 16 AND byteLength >= prelude totalLength.","Never parse raw reader chunks directly — route them through a frame-boundary assembler.","Treat a sub-16-byte chunk at stream end as a truncation signal and retry, not as a fatal bug.","Test the executor against artificially truncated bodies to confirm graceful handling."],"tags":["eventstream","binary-protocol","streaming","kiro","corrupt-frame"],"backgroundTag":"eventstream-truncated-frame","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}