denoland / deno

A modern runtime for JavaScript and TypeScript.
https://deno.com
MIT License
98.05k stars 5.39k forks source link

Piping file stream triggers spurious uncaught exception in debugger #18513

Open rotu opened 1 year ago

rotu commented 1 year ago

The following code runs fine without a debugger attached, but if the Node debugger is attached (via either VSCode or Google Chrome), and "pause on uncaught exceptions" is enabled, then the debugger trips with the error message "TypeError: The writer has already been released."

deno run --inspect-wait --allow-all ./repro.ts

const DEMO_FILE = await Deno.makeTempFile()
const file = await Deno.open(DEMO_FILE, { read: true, write: false })
await file.readable.pipeTo(new WritableStream())

reject (ext:deno_web/06_streams.js:109) writableStreamDefaultWriterEnsureReadyPromiseRejected (ext:deno_web/06_streams.js:4165) writableStreamDefaultWriterRelease (ext:deno_web/06_streams.js:4194) finalize (ext:deno_web/06_streams.js:2544) (anonymous) (ext:deno_web/06_streams.js:2532) Promise.then (async) uponPromise (ext:deno_web/06_streams.js:192) uponFulfillment (ext:deno_web/06_streams.js:167) shutdown (ext:deno_web/06_streams.js:2530) (anonymous) (ext:deno_web/06_streams.js:2426) Promise.then (async) uponPromise (ext:deno_web/06_streams.js:192) uponFulfillment (ext:deno_web/06_streams.js:167) isOrBecomesClosed (ext:deno_web/06_streams.js:2484) readableStreamPipeTo (ext:deno_web/06_streams.js:2420) pipeTo (ext:deno_web/06_streams.js:4857) (anonymous) (repro.ts:19)

Screenshot 2023-03-30 at 15 28 47

deno --version deno 1.32.1 (release, aarch64-apple-darwin) v8 11.2.214.9 typescript 5.0.2

rotu commented 1 year ago

This does not seem to trigger a unhandledrejection handler, which is puzzling. Aha! it looks like it's because after rejecting the promise, we mark that promise as handled. But the V8 debugger doesn't get this memo. https://github.com/denoland/deno/blob/772201449713fbefad6c42b9ce545a5bb2d7499b/ext/web/06_streams.js#L4208-L4218

Seems to have been caused by https://github.com/denoland/deno/pull/9103

Seems closely related to: https://github.com/whatwg/streams/issues/547 https://bugs.chromium.org/p/chromium/issues/detail?id=654701 https://bugzilla.mozilla.org/show_bug.cgi?id=1561911


Resolving may be as simple as moving setPromiseIsHandledToTrue before the calls to Deferred.reject or right after the Deferred is constructed.

Though that still seems only partial. The debugger treats that as a caught exception, whereas I'd expect normal pipe closure to not create an exception at all.