{"record":{"id":"b5544304ecb2649c","repo":"Hmbown/CodeWhale","slug":"runtime-event-stream-ended-inside-a-frame","errorCode":null,"errorMessage":"Runtime event stream ended inside a frame","messagePattern":"Runtime event stream ended inside a frame","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"npm/runtime-sdk/index.js","lineNumber":255,"sourceCode":"async function* parseEventStream(body, { maxFrameChars = Infinity, requireBoundary = false } = {}) {\n  const decoder = new TextDecoder(\"utf-8\", { fatal: requireBoundary });\n  let buffer = \"\";\n  for await (const chunk of body) {\n    buffer += decoder.decode(chunk, { stream: true });\n    let boundary;\n    while ((boundary = eventStreamBoundary(buffer)) !== null) {\n      if (boundary.index > maxFrameChars) throw new Error(\"Runtime event frame exceeds the size limit\");\n      const frame = buffer.slice(0, boundary.index);\n      buffer = buffer.slice(boundary.index + boundary.length);\n      const event = parseSseFrame(frame);\n      if (event !== undefined) {\n        yield event;\n      }\n    }\n    if (buffer.length > maxFrameChars) throw new Error(\"Runtime event frame exceeds the size limit\");\n  }\n  buffer += decoder.decode();\n  if (requireBoundary && buffer.trim()) throw new Error(\"Runtime event stream ended inside a frame\");\n  const event = parseSseFrame(buffer);\n  if (event !== undefined) {\n    yield event;\n  }\n}\n\nfunction eventStreamBoundary(buffer) {\n  const lf = buffer.indexOf(\"\\n\\n\");\n  const crlf = buffer.indexOf(\"\\r\\n\\r\\n\");\n  if (lf < 0 && crlf < 0) {\n    return null;\n  }\n  if (crlf >= 0 && (lf < 0 || crlf < lf)) {\n    return { index: crlf, length: 4 };\n  }\n  return { index: lf, length: 2 };\n}\n","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/npm/runtime-sdk/index.js#L237-L273","documentation":"With requireBoundary enabled (threadEvents uses it), the stream must end exactly on a frame boundary. If buffered text remains after the connection closes and final decoding, parseEventStream() throws, indicating a truncated or corrupt SSE stream rather than silently dropping a partial event.","triggerScenarios":"The runtime or an intermediary closes the TCP connection mid-frame — network drop, proxy timeout, server crash, or a runtime that writes a partial event and exits. threadEvents passes requireBoundary: true.","commonSituations":"Load balancer idle/short timeouts killing long SSE connections; container OOM-killed mid-write; client machine sleeping during a long replay.","solutions":["Reconnect and resume the replay with { sinceSeq: lastProcessedSeq } to get the missing tail.","Add retry/backoff around stream consumption for transient network drops.","Check intermediary (proxy/LB) timeouts and raise them for long-lived SSE connections.","Investigate runtime logs if truncation recurs — the server likely crashed mid-frame."],"exampleFix":"// before\nfor await (const ev of client.threadEvents(id)) handle(ev);\n// after\nlet since;\nfor (;;) {\n  try {\n    for await (const ev of client.threadEvents(id, { sinceSeq: since })) {\n      handle(ev); since = ev.seq;\n    }\n    break;\n  } catch (err) {\n    if (!err.message.includes(\"ended inside a frame\")) throw err;\n    await sleep(500);\n  }\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"let since;\nwhile (true) {\n  try {\n    for await (const ev of client.threadEvents(id, { sinceSeq: since })) { handle(ev); since = ev.seq; }\n    break;\n  } catch (err) {\n    if (!err.message.includes(\"ended inside a frame\")) throw err;\n    await backoff();\n  }\n}","preventionTips":["Always resume replays with sinceSeq set to the last processed event so truncation is recoverable.","Raise proxy/load-balancer idle timeouts for long-lived SSE connections.","Monitor runtime liveness; repeated truncation usually means the server is crashing mid-frame."],"tags":["sse","truncated-stream","streaming","network"],"backgroundTag":"broken-pipe","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}