{"record":{"id":"fa559caf1fd8609e","repo":"clockworklabs/SpacetimeDB","slug":"option-stream-not-supported","errorCode":null,"errorMessage":"Option 'stream' not supported","messagePattern":"Option 'stream' not supported","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/core/src/host/v8/builtins/text_encoding.js","lineNumber":57,"sourceCode":"  }\n\n  get encoding() {\n    return this.#encoding;\n  }\n  get fatal() {\n    return this.#fatal;\n  }\n  get ignoreBOM() {\n    return false;\n  }\n\n  /**\n   * @argument {any} input\n   * @argument {any} options\n   */\n  decode(input, options = {}) {\n    if (options.stream) {\n      throw new TypeError(\"Option 'stream' not supported\");\n    }\n    if (input instanceof ArrayBuffer || input instanceof SharedArrayBuffer) {\n      input = new Uint8Array(input);\n    }\n    return utf8_decode(input, this.#fatal);\n  }\n};\n","sourceCodeStart":39,"sourceCodeEnd":65,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/core/src/host/v8/builtins/text_encoding.js#L39-L65","documentation":"The TextDecoder polyfill in the SpacetimeDB V8 host has no streaming decode state: decode(input, options) throws TypeError('Option stream not supported') whenever options.stream is truthy. Each decode() call is independent, so incremental decoding must be done by the caller.","triggerScenarios":"Calling decoder.decode(chunk, { stream: true }) while feeding chunked data (websocket frames, file reads, socket streams) into the decoder.","commonSituations":"Porting streaming parsers from browsers/Node that rely on BOM/state carry-over between decode calls; handling large payloads split across messages.","solutions":["Accumulate chunks in a buffer and call decode() once, without options, when the message is complete","If every chunk is message-complete and ends on a UTF-8 codepoint boundary (true for frames you framed yourself), call decode(chunk) with no options","Move incremental decoding of untrusted/boundary-split streams outside the module"],"exampleFix":"// before\nfor (const chunk of chunks) parts.push(dec.decode(chunk, { stream: true }));\n\n// after\nlet buf = new Uint8Array(0);\nfor (const chunk of chunks) {\n  const merged = new Uint8Array(buf.length + chunk.length);\n  merged.set(buf); merged.set(chunk, buf.length);\n  buf = merged;\n}\nconst text = dec.decode(buf);","handlingStrategy":"validation","validationCode":"function decodeAll(dec: TextDecoder, chunks: Uint8Array[]): string {\n  if (chunks.length === 1) return dec.decode(chunks[0]);\n  const total = chunks.reduce((n, c) => n + c.length, 0);\n  const buf = new Uint8Array(total);\n  let off = 0;\n  for (const c of chunks) { buf.set(c, off); off += c.length; }\n  return dec.decode(buf); // no options: stream is unsupported\n}","typeGuard":"function isStreamSafe(chunks: Uint8Array[]): boolean {\n  // Safe only if each chunk is a complete message; conservative default: false\n  return chunks.length <= 1;\n}","tryCatchPattern":"try {\n  text = dec.decode(chunk, options);\n} catch (e) {\n  if (e instanceof TypeError && /stream/.test(e.message)) {\n    pending.push(chunk); // buffer and decode once complete\n  } else throw e;\n}","preventionTips":["Design protocols so each message is a complete UTF-8 payload","Never pass option objects to decode() verbatim; drop stream explicitly","Buffer chunks and decode once at message end instead of incrementally"],"tags":["javascript","textdecoder","encoding","streaming","polyfill","v8"],"backgroundTag":"streaming-decode-unsupported","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}