Skip to content

stream.pipeline does not invoke callback when error happens #39447

Description

@mightyaleksey

Version

v15.8.0

Platform

Darwin Alekseys-iMac.local 20.5.0 Darwin Kernel Version 20.5.0: Sat May 8 05:10:33 PDT 2021; root:xnu-7195.121.3~9/RELEASE_X86_64 x86_64

Subsystem

stream

What steps will reproduce the bug?

Run the following code:

'use strict'

const { Transform, pipeline } = require('stream')

function createTransformStream (tf, context) {
  return new Transform({
    readableObjectMode: true,
    writableObjectMode: true,

    transform (chunk, encoding, done) {
      tf(chunk, context, done)
    }
  })
}

const ts = createTransformStream((chunk, _, done) => done(new Error('Artificial error')))

pipeline(ts, process.stdout, (err) => {
  if (err) console.log(err)
  console.log('done')
})

console.log('run test')
ts.write('test')

How often does it reproduce? Is there a required condition?

all the time

What is the expected behavior?

I expect pipeline callback to be called with error argument. So I'll have the output like this:

run test
Error: Artificial error
    at /Users/hackerman/Documents/code/wave/demo.js:16:59
    at Transform.transform [as _transform] (/Users/hackerman/Documents/code/wave/demo.js:11:7)
    at Transform._write (node:internal/streams/transform:184:23)
    at writeOrBuffer (node:internal/streams/writable:400:12)
    at _write (node:internal/streams/writable:341:10)
    at Transform.Writable.write (node:internal/streams/writable:345:10)
    at Object.<anonymous> (/Users/hackerman/Documents/code/wave/demo.js:24:4)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1137:10)
    at Module.load (node:internal/modules/cjs/loader:973:32)
done

What do you see instead?

pipeline callback is not called. Output that I have:

run test

Additional information

When I run mentioned code with --inspect-brk flag — I can achieve expected behaviour in google chrome console. Steps:

  • run node --inspect-brk demo.js
  • open chrome://inspect/#devices in chrome
  • click inspect and resume script execution

Activity

  1. changed the title [-]stream.pipeline does not invoke callback[/-] [+]stream.pipeline does not invoke callback when error happens[/+] on Jul 19, 2021
  2. added
    streamIssues and PRs related to Node.js streams.
    on Jul 19, 2021
  3. targos commented on Jul 19, 2021

    @targos
    Member

    @nodejs/streams

  4. mcollina commented on Jul 19, 2021

    @mcollina
    SponsorMember

    The problem is that process.stdout() never gets closed.

    'use strict'
    
    const { Transform, Writable, pipeline } = require('stream')
    
    const w = new Writable({
      write (chunk, enc, cb) {
        cb()
      }
    })
    
    function createTransformStream (tf, context) {
      return new Transform({
        readableObjectMode: true,
        writableObjectMode: true,
    
        transform (chunk, encoding, done) {
          tf(chunk, context, done)
        }
      })
    }
    
    const ts = createTransformStream((chunk, _, done) => done(new Error('Artificial error')))
    
    pipeline(ts, w, (err) => {
      if (err) console.log(err)
      console.log('done')
    })
    
    console.log('run test')
    ts.write('test')

    In theory, this should have been solved by #32373, but I guess there is something different from using a child_process and a tty.

    @ronag wdyt?

  5. added
    confirmed-bugIssues and PRs for confirmed bugs.
    good first issueIssues that are suitable for first-time contributors.
    on Jul 19, 2021
  6. mightyaleksey commented on Jul 19, 2021

    @mightyaleksey
    Author

    Oh, makes sense. Thanks @mcollina for clarifying this issue for me.

  7. ktfth commented on Jul 23, 2021

    @ktfth

    @mcollina I have some questions...
    Can i work on that issue?
    Where is located the entry points for streams "Transformer"?
    I can put the test on test folder and the execution happen because the existance of a mechanism for execution?

  8. ktfth commented on Jul 24, 2021

    @ktfth

    I'm made some progress investigating the code base, but if it's possible to talk more about that could be helpful.

  9. ktfth commented on Jul 25, 2021

    @ktfth

    I have made a change who passes on tests with the case described by you @mcollina here some refs: https://github.com/ktfth/node/tree/fix/stream-pipeline-error-callback

  10. mcollina commented on Jul 25, 2021

    @mcollina
    SponsorMember

    You need to add a test for stdout. Look in https://github.com/nodejs/node/tree/master/test/pseudo-tty.

  11. ktfth commented on Jul 25, 2021

    @ktfth

    Included another test with process.stdout, but looking the reference you shared @mcollina to create more tests

  12. ktfth commented on Jul 25, 2021

    @ktfth

    Do you have an hint to what I can search for to made a good test?

  13. 28 remaining items

  14. added a commit that references this issue on Aug 5, 2021
  15. mcollina commented on Aug 5, 2021

    @mcollina
    SponsorMember

    After doing some work, I think this is not a bug. The following pass:

    'use strict'
    
    const { Transform, pipeline } = require('stream')
    
    function createTransformStream (tf, context) {
      return new Transform({
        readableObjectMode: true,
        writableObjectMode: true,
    
        transform (chunk, encoding, done) {
          tf(chunk, context, done)
        }
      })
    }
    
    const ts = createTransformStream((chunk, _, done) => done(new Error('Artificial error')))
    
    process.stdout.on('error', function () {
      process._rawDebug('error emitted')
    })
    
    
    process.stdout.on('close', function () {
      process._rawDebug('close emitted')
    })
    
    pipeline(ts, process.stdout, (err) => {
      if (err) process._rawDebug(err)
      process._rawDebug('done')
    })
    
    console.log('run test')
    ts.write('test')

    The reason why you do not see the output is that process.stdout is closed. Maybe we should print a warning in this case (or maybe console.log() should bypass it.

  16. rluvaton commented on Aug 5, 2021

    @rluvaton
    Member

    After doing some work, I think this is not a bug.

    [...]

    The reason why you do not see the output is that process.stdout is closed. Maybe we should print a warning in this case (or maybe console.log() should bypass it.

    According to #7606 (comment) stdio should never be closed

  17. mcollina commented on Aug 5, 2021

    @mcollina
    SponsorMember

    the actual file descriptor is never closed. However the stream object process.stdout can be closed as it is a stream.
    Otherwise the callback to pipeline would never be called.

  18. ktfth commented on Aug 5, 2021

    @ktfth

    I understand and totally agree

  19. rluvaton commented on Aug 5, 2021

    @rluvaton
    Member

    the actual file descriptor is never closed. However the stream object process.stdout can be closed as it is a stream.
    Otherwise the callback to pipeline would never be called.

    Closing the stdio can have an unwanted results:

    1. If you run the code in the issue above in REPL you wouldn't see any character you type after that ran
    2. Loggers that use process.stdout would not output anything if it will close.

    I'm sure there are more reasons, this is just at the top of my head.

    I think we should fix the pipeline rather than the process.stdout

  20. mcollina commented on Aug 5, 2021

    @mcollina
    SponsorMember

    Here is an alternative way to fix this: #39670

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    confirmed-bugIssues and PRs for confirmed bugs.good first issueIssues that are suitable for first-time contributors.streamIssues and PRs related to Node.js streams.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions