napi-rs/napi-rs · error

Failed to schedule ReadableStream read

Error message

Failed to schedule ReadableStream read

What it means

This error is returned from `Reader::poll_next` when the N-API call that schedules an async read on the underlying ReadableStream returns a non-Ok status. The stream is terminated (`done = true`) and the raw scheduling status is surfaced with this message. It means the runtime refused to start the read operation, not that the stream itself errored.

Solutions

  1. Inspect the attached `status` field for the exact N-API failure code
  2. Clear or handle any pending JS exception before polling the stream
  3. Ensure the Reader is polled while the environment is alive and on the JS thread
  4. Upgrade/verify Node version and N-API compatibility of the embedding

Example fix

// before
while let Some(v) = stream.next().await { ... } // pending exception present
// after
// (JS side) ensure prior awaited code did not throw before consuming the stream
try { for await (const c of readable) {} } catch (e) { /* handle */ }
Defensive patterns

Strategy: try-catch

Type guard

// Rust: check terminal state before polling again
if reader_is_done(&stream) { return; }

Try / catch

match stream.next().await { Err(e) if e.message == "Failed to schedule ReadableStream read" => { inspect(e.status); stop_polling(); } , other => other }

Prevention

When it happens

Trigger: Calling next()/poll_next on a stream obtained from a ReadableStream when the scheduling napi call fails — e.g. an exception is pending in the environment, the env is shutting down, or an invalid status is returned by the runtime.

Common situations: Awaiting the stream inside a context with a pending uncaught JS exception; using the Reader during process/env teardown; embedding Node with mismatched or failing N-API environment.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of napi-rs/napi-rs@39bd1205e4 (2026-09-13). Data as JSON: /api/errors/50cf2752c092e0d1. Report an issue: GitHub.

Appendix: source

Thrown at crates/napi/src/bindgen_runtime/js_values/stream/read.rs:789

            }
          }
          Ok(())
        },
      );
      // The threadsafe call itself can fail to schedule (runtime shutting down /
      // `Status::Closing`, or a full queue). When it does, the callback above never
      // runs, so `reading`/`waker` would be stuck. Recover synchronously: clear the
      // flag, end the stream, and surface the error now.
      if status != Status::Ok {
        let mut inner = self
          .state
          .inner
          .lock()
          .map_err(|_| Error::new(Status::InvalidArg, "Poisoned lock in Reader::poll_next"))?;
        inner.reading = false;
        inner.done = true;
        inner.waker = None;
        return Poll::Ready(Some(Err(Error::new(
          status,
          "Failed to schedule ReadableStream read",
        ))));
      }
    }

    Poll::Pending
  }
}

/// Shared state for ReadableStream that coordinates between pull and cancel callbacks.
/// Uses Arc to share ownership between callbacks, Mutex to protect the stream,
/// and AtomicBool for lock-free cancellation checks.
///
/// Memory management: The Arc is freed by a invoke when the underlying_source
/// object is garbage collected. Callbacks only "borrow" the Arc using the
/// increment+from_raw pattern, never freeing it directly. This prevents
/// use-after-free if cancel_callback is invoked after pull_callback has

View on GitHub (pinned to 39bd1205e4)