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
- Inspect the attached `status` field for the exact N-API failure code
- Clear or handle any pending JS exception before polling the stream
- Ensure the Reader is polled while the environment is alive and on the JS thread
- 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
- Ensure no pending JS exception exists before consuming the stream
- Only poll during normal env lifetime, not teardown
- Verify N-API version compatibility of the runtime
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 hasView on GitHub (pinned to 39bd1205e4)