[BUG] Pipeline stream unexpectly being closed when using `extract`
What / Why
When you attempt to extract a tarball through streams sometimes it will be closed with an error: [ERR_STREAM_PREMATURE_CLOSE]: Premature close, although the files are successfully extracted. No Errors Codes are shown in the process.
It seems to happen only on newer versions of Node > v18
Looking at the spec it seems now that streams that emit a close event before a end event trigger this error, so worth taking a look on that.
When
- n/a
Where
- n/a
How
Current Behavior
- It throws an
ERR_STREAM_PREMATURE_CLOSEError when trying to extract
Steps to Reproduce
$ nvm install 18.7.0
$ npm install tar got
$ mkdir output
import stream from 'node:stream';
import { promisify } from 'util';
import tar from 'tar';
import got from 'got';
const pipeline = promisify(stream.pipeline);
pipeline(
got.stream('https://codeload.github.com/npm/node-tar/tar.gz/main'),
tar.extract({ cwd: './output' }, ['node-tar-main/lib'])
)
Expected Behavior
- It should extract the content of the archive without any issues
References
- Related to Next.js Issue #39321
does this mean instead of https://github.com/npm/node-tar/blob/9d71c5673683d6309c75e6a85e78fa285dbe9a2d/lib/parse.js#L85-L94 you would do
this.on('end', () => setTimeout(() => this.emit('close')))
if (opt.ondone) {
this.on(DONE, opt.ondone)
} else {
this.on(DONE, _ => {
this.emit('prefinish')
this.emit('finish')
this.emit('end')
})
}
essentially? @jeferson-sb @lukekarrys
that seems on odd addition :/
@webark yeah looks like it is related to that one line
meant to say "odd" addition. I was going to try to add a test case.
I tried adding this test
t.test('ensure an open stream is not prematuraly closed', t => {
const file = path.resolve(tars, 'body-byte-counts.tar')
const dir = path.resolve(extractdir, 'basic-with-stream')
t.beforeEach(async () => {
await rimraf(dir)
await mkdirp(dir)
})
const check = async t => {
t.equal(fs.lstatSync(path.resolve(dir, '1024-bytes.txt')).size, 1024)
await rimraf(dir)
t.end()
}
t.test('async promisey', t => {
return pipeline(
fs.createReadStream(file),
x({ cwd: dir }, ['1024-bytes.txt'])
).then(_ => check(t))
})
t.end()
})
But it is still passing without any modifications to the code 🤔😔 Any ideas?
I'm experiencing this. Is there a workaround or an expected fix?
I opened up the PR #332 which fixes the issue. I'm using my fork in a project and it is working. Hopefully it will get merged and released soon.