Skip to content

[Bug]: Unhandled promise rejection in fetch().pipe() when response stream errors #1443

@daguimu

Description

@daguimu

What happened?

The responseToReadable function in goods.ts assigns an async function to rs._read, but doesn't wrap it in a try/catch. If reader.read() throws (e.g., network disconnection, aborted request, or corrupted stream), the promise rejection is unhandled and can crash the Node.js process.

rs._read = async () => {
  const result = await reader.read()  // Can throw - unhandled
  rs.push(result.done ? null : Buffer.from(result.value))
}

How it should work?

Errors from reader.read() should be caught and propagated to the stream via rs.destroy(err), allowing downstream consumers to handle the error gracefully.

How to reproduce the bug?

import { fetch } from 'zx'

// Start a fetch that will be aborted mid-stream
const controller = new AbortController()
const result = fetch('https://speed.hetzner.de/100MB.bin', { signal: controller.signal })

const p = result.pipe`cat`

// Abort after a short delay to trigger the error in _read
setTimeout(() => controller.abort(), 100)

await p // Unhandled promise rejection from _read

Version

8.9.0

What's OS kind?

All

What JS runtime is used?

Node.js

Runtime Version

22.x

Error stack / relevant log output

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()

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions