gulp-decompress icon indicating copy to clipboard operation
gulp-decompress copied to clipboard

Potential UnhandledPromiseRejection?

Open CodeMan99 opened this issue 7 years ago • 2 comments

In your readme example I believe there is potentially an UnhandledPromiseRejection.

The Transform stream has no error handler so I suspect the synchronous call to the flush callback will cause it to re-throw inside the promise scope.

CodeMan99 avatar Mar 15 '18 20:03 CodeMan99

@CodeMan99 What is the recommended fix? I believe I am running into this problem.

It works with gulp-download but I am trying to upgrade to gulp-download2.

This works:

const gulp = require('gulp');
const download = require('gulp-download2');

gulp.task('download_server', function (done) {
	downloadFoo();
	done();
});

async function downloadFoo() {
	download("https://my.url/file.zip")
		.pipe(gulp.dest('./server'));
}

As soon as I add decompress:

...
const decompress = require('gulp-decompress');
...

async function downloadFoo() {
	download("https://my.url/file.zip")
		.pipe(decompress())
		.pipe(gulp.dest('./server'));
}

It fails with:

[11:10:29] Starting 'download_server'...
[11:10:29] Finished 'download_server' after 5.42 ms
[11:10:29] Downloading https://my.url/file.zip...
node:internal/process/promises:392
      new UnhandledPromiseRejection(reason);
      ^

UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "[object Object]".
    at throwUnhandledRejectionsMode (node:internal/process/promises:392:7)
    at processPromiseRejections (node:internal/process/promises:475:17)
    at process.processTicksAndRejections (node:internal/process/task_queues:106:32) {
  code: 'ERR_UNHANDLED_REJECTION'
}

Node.js v22.11.0

guw avatar Nov 09 '24 10:11 guw

@guw Basically, don't call cb synchronously.

The reason why this is needed is because the transform callback will internally re-throw the error. I don't exactly remember the reason for this (anti-)pattern. It certainly has been a hot minute. Anyway, this is a problem for any Promise pattern since everything is implicitly wrapped in try/catch.

I would get around this with setImmediate.

decompress(...)
    .then(files => {
        // ... happy path ... an error thrown here is sent to the `.catch` handler
    })
    .catch(err => {
        // ... error path ... an error thrown here is sent to the promise chain (unhandled)
        setImmediate(() => {
            // ... next tick path ... an error thrown here is dropped
            //     which is OK since the callback is the error handling
            cb(new PluginError('gulp-decompress:', err, {fileName: file.path}));
        })
    })

CodeMan99 avatar Nov 09 '24 16:11 CodeMan99