Fix reading an object so it no longer panics on a JetStream timeouts in Object Store
`Object`'s `AsyncRead` impl creates its ordered consumer lazily on the
first poll and `unwrap()`s the result. A JetStream request that ages past
the client's timeout therefore panics inside a runtime worker rather than
surfacing as an error — and in a binary built with `panic = "abort"` that
takes the whole process down, along with every unrelated in-flight
request.
Observed in production: a front-door process aborted (SIGABRT) mid-fetch,
twice within a minute, each time killing connections belonging to other
callers. The build the fetch belonged to had already succeeded
server-side.
Two unwraps are on that path and both are removed:
- `create_consumer(...).unwrap()` becomes a `map_err` into `StreamError`,
preserving the timeout classification (`ConsumerErrorKind::TimedOut` →
`StreamErrorKind::TimedOut`) rather than flattening every failure to
`Other`. The future's declared output is already
`Result<Ordered, StreamError>`, so nothing above changes shape.
- `subscription.unwrap()` in the poll becomes a `map_err` into
`io::Error`, which is what the surrounding `poll_read` already returns
for subscription failures a few lines below.
Removing the panic leaves two further properties to keep:
- The future is taken out of the `Object` before it is polled and put
back only while it is still pending. Left in place after it resolved,
it would be polled again by a caller that retries the read, and polling
a completed `async` block panics ("`async fn` resumed after
completion") — the same abort, one step further along. A retry now
builds a fresh future and asks the server again.
- The classification survives the `io::Error` boundary, which is all an
`AsyncRead` caller ever sees: a timeout arrives as
`ErrorKind::TimedOut` rather than `Other`, with the `StreamError`
retained as its source.
Both are covered against a real server. A read whose consumer creation
fails (the bucket is removed between `get` and the first read) is an
error on the second read too, rather than a panic; a read whose request
has nobody left to answer it comes back as `ErrorKind::TimedOut`.
A reader now gets `Err` where it previously got a dead process. No public
API change.
Refs nats-io/nats.rs#1615. M
macrauder committed
b125cf3bb6014e1148d20fab469d2910c19d1d53
Parent: 7d4423f
Committed by GitHub <noreply@github.com>
on 9/25/2026, 5:35:29 AM