{"record":{"id":"6398a7af01d04248","repo":"denoland/deno","slug":"readablestream-is-locked","errorCode":null,"errorMessage":"ReadableStream is locked","messagePattern":"ReadableStream is locked","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/http/01_http.js","lineNumber":300,"sourceCode":"        ) {\n          await respBody.cancel(error);\n        }\n        throw error;\n      }\n\n      if (isStreamingResponseBody) {\n        let success = false;\n        if (\n          respBody === null ||\n          !ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, respBody)\n        ) {\n          throw new TypeError(\"Unreachable\");\n        }\n        const resourceBacking = getReadableStreamResourceBacking(respBody);\n        let reader;\n        if (resourceBacking) {\n          if (respBody.locked) {\n            throw new TypeError(\"ReadableStream is locked\");\n          }\n          reader = respBody.getReader(); // Acquire JS lock.\n          try {\n            await op_http_write_resource(\n              writeStreamRid,\n              resourceBacking.rid,\n            );\n            if (resourceBacking.autoClose) core.tryClose(resourceBacking.rid);\n            readableStreamClose(respBody); // Release JS lock.\n            success = true;\n          } catch (error) {\n            const connError = httpConn[connErrorSymbol];\n            if (\n              ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) &&\n              connError != null\n            ) {\n              // deno-lint-ignore no-ex-assign\n              error = new connError.constructor(connError.message);","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/http/01_http.js#L282-L318","documentation":"On the streaming-response path, when the response body is backed by an op resource (getReadableStreamResourceBacking returns non-null, e.g. a file or socket-backed stream), Deno must acquire the JS-side stream lock with getReader() to hand the resource to op_http_write_resource. If respBody.locked is already true someone else holds a reader and the transfer cannot proceed.","triggerScenarios":"Calling response.body.getReader() (or pipeThrough/pipeTo, or response.body.tee()) and not releasing it before respondWith; piping a resource-backed stream (from Deno.open / Deno.stdin / a socket) into the Response while a consumer still holds a reader; teeing a resource-backed stream (tee locks and reads it).","commonSituations":"Progress meters that attach a reader to file/socket streams; combining tee() with streaming responses; duplex piping where one side keeps the reader; re-serving a stream after a cancelled request without recreating it.","solutions":["Don't take a reader on the body you pass to respondWith; let Deno stream it.","If you need to observe the stream, read it fully into memory or tee and consume only one branch.","For resource-backed streams (files, sockets), prefer returning the stream directly in new Response(stream) and avoid any intermediate reader/lock.","Release locks you no longer need: reader.releaseLock() before responding (only if no read is pending)."],"exampleFix":"// before\nconst body = file.readable;\nconst reader = body.getReader(); // lock held\nhttpConn.respondWith(new Response(body));\n\n// after\nconst body = file.readable;\nhttpConn.respondWith(new Response(body)); // no reader taken","handlingStrategy":"validation","validationCode":"const body = resp.body;\nif (body instanceof ReadableStream && body.locked) {\n  throw new Error(\"response body stream is locked; recreate the stream before responding\");\n}","typeGuard":"function isUnlockedStream(s: unknown): s is ReadableStream { return s instanceof ReadableStream && !s.locked; }","tryCatchPattern":"try { await httpConn.respondWith(new Response(stream)); } catch (e) { if (e instanceof TypeError && e.message === \"ReadableStream is locked\") { httpConn.close(); return; } throw e; }","preventionTips":["Don't call getReader()/pipeTo()/tee() on streams destined for the response.","For file/socket streams, pass Deno.open(...).readable directly into new Response().","On request cancellation, recreate streams instead of re-serving disturbed ones."],"tags":["http","streams","locked-stream","resources"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}