{"record":{"id":"1048d3b9681d7077","repo":"tldraw/tldraw","slug":"could-not-stream-object-obj-key","errorCode":null,"errorMessage":"Could not stream object ${obj.Key}","messagePattern":"Could not stream object (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"internal/scripts/deploy-dotcom.ts","lineNumber":1063,"sourceCode":"\t\tobjectsToFetch.map((k) => k.Key)\n\t)\n\tfor (const obj of objectsToFetch) {\n\t\tconst { Body } = await R2.send(\n\t\t\tnew GetObjectCommand({\n\t\t\t\tBucket: R2_BUCKET,\n\t\t\t\tKey: obj.Key,\n\t\t\t})\n\t\t)\n\t\tif (!Body) {\n\t\t\tthrow new Error(`Could not fetch object ${obj.Key}`)\n\t\t}\n\t\t// pipe into untar\n\t\t// `keep-existing` is important here because we don't want to overwrite the new assets\n\t\t// if they have the same name as the old assets becuase they will have different sentry debugIds\n\t\t// and it will mess up the inline source viewer on sentry errors.\n\t\tconst out = tar.x({ cwd: assetsDir, 'keep-existing': true })\n\t\tif (!Body?.transformToWebStream) {\n\t\t\tthrow new Error(`Could not stream object ${obj.Key}`)\n\t\t}\n\t\tfor await (const chunk of Body.transformToWebStream() as any as AsyncIterable<Uint8Array>) {\n\t\t\tout.write(Buffer.from(chunk.buffer))\n\t\t}\n\t\tout.end()\n\t}\n}\n\nmain().catch(async (err) => {\n\t// don't notify discord on preview builds\n\tif (env.TLDRAW_ENV !== 'preview') {\n\t\tawait discord.message(`${Discord.AT_TEAM_MENTION} Deploy failed: ${err.stack}`, {\n\t\t\talways: true,\n\t\t})\n\t}\n\tconsole.error(err)\n\tprocess.exit(1)\n})","sourceCodeStart":1045,"sourceCodeEnd":1081,"githubUrl":"https://github.com/tldraw/tldraw/blob/b31086b44731a7d1d9d46be4163ac8ef7417321d/internal/scripts/deploy-dotcom.ts#L1045-L1081","documentation":"After confirming Body exists, the script requires Body.transformToWebStream to pipe the R2 object into tar extract. Older AWS SDK v2 style bodies (Readable streams) lack transformToWebStream; the guard catches that incompatibility before attempting iteration.","triggerScenarios":"Using a stream type that doesn't expose transformToWebStream (AWS SDK v2, or a mocked/test double); SDK downgrade or polyfill removing the method.","commonSituations":"@aws-sdk/client-s3 major version mismatch; running the script under a Node version or runtime without web streams; third-party S3-compatible client returning a Node Readable.","solutions":["Ensure @aws-sdk/client-s3 v3 (modular) is installed at the version declared in the workspace.","If using an alternate S3 client, wrap its stream: `Readable.toWeb(Body)` to produce a web stream.","Upgrade Node to >=22.12 (repo requirement) for native web stream support."],"exampleFix":"// before\nif (!Body?.transformToWebStream) {\n  throw new Error(`Could not stream object ${obj.Key}`)\n}\n// after (fall back to Node stream conversion)\nconst webStream = Body.transformToWebStream\n  ? Body.transformToWebStream()\n  : Readable.toWeb(Body)","handlingStrategy":"fallback","validationCode":"// Detect stream capability before iterating\nif (typeof Body?.transformToWebStream !== 'function' && typeof Body?.pipe !== 'function') {\n  throw new Error(`Object body has no usable stream interface: ${obj.Key}`)\n}","typeGuard":"function isWebStreamBody(Body: unknown): Body is { transformToWebStream: () => ReadableStream } {\n  return Boolean(Body && typeof (Body as any).transformToWebStream === 'function')\n}","tryCatchPattern":null,"preventionTips":["Pin @aws-sdk/client-s3 v3 so Body always exposes transformToWebStream.","Maintain a Node-version >= 22.12 requirement (already a repo rule) for web stream support.","If you support alternate S3 clients, normalize the body to a web stream via Readable.toWeb."],"tags":["deploy","r2","aws-sdk","streams","node"],"backgroundTag":null,"analyzedSha":"b31086b44731a7d1d9d46be4163ac8ef7417321d","analyzedAt":"2026-08-12T17:05:39.947Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}