RabbitMQ Error: Stream Consumer 'offset out of range' / Invalid Offset Spec
Fix a RabbitMQ stream consumer failing with 'offset out of range' or an invalid offset spec: understand retention-trimmed offsets, first/last/next/timestamp specs and how to attach safely.
- #rabbitmq
- #messaging
- #troubleshooting
- #errors
Stuck on this RabbitMQ error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Exact Error Message
A stream consumer that asks for an offset the stream no longer holds (or a malformed offset spec) is refused at attach time. Clients surface it from the stream protocol and the broker logs the subscribe failure:
io.rabbitmq.stream.StreamException: Subscription error:
subscription id 1, response code 'offset out of range' (0x000d)
2026-07-17 11:20:14.902 [warning] <0.1044.0> Reader for stream 'events' rejected
subscribe: requested offset 500000 is out of range (stream first offset 812340,
last offset 991027)
A bad specification string fails differently, before the offset is even resolved:
Error: invalid offset specification "lastest"
(expected one of: first, last, next, an absolute offset, or a timestamp)
What It Means
A RabbitMQ stream is an append-only log. Every message has a monotonic offset, and consumers attach at an offset specification: first, last, next, an absolute integer offset, or a timestamp. Unlike classic queues, a stream is not consumed away — messages stay until retention (max age or max length) trims the oldest segments.
offset out of range means the consumer asked for an offset that no longer exists in the stream — almost always because retention already deleted that segment, so the requested offset is below the stream’s current first offset. invalid offset specification is different: the spec string itself is not one of the accepted forms (a typo like lastest, or an unparseable timestamp).
Common Causes
- The consumer stored an absolute offset, then retention (max-age / max-length-bytes) trimmed past it before the consumer reconnected.
- The stream was recreated or truncated, resetting its first offset above the stored value.
- A hard-coded absolute offset (e.g.
0) after the head has already been trimmed. - A malformed offset spec: a typo (
lastest), or a timestamp in the wrong unit/format. - Offset tracking was never committed, so the client falls back to a stale or zero offset.
- Clock skew making a
timestampspec resolve outside the retained window.
Diagnostic Commands
Inspect the stream’s committed offset boundaries and members:
rabbitmq-streams stream_status events
List consumers and the offset each is tracking:
rabbitmq-streams list_stream_consumers -p /
Check the stream’s declared retention arguments and current message count:
rabbitmqctl list_queues name type messages arguments | grep -i stream
Read the stored server-side offset for a named reference (subscription name), if the client uses offset tracking:
rabbitmq-streams stream_status --track-by consumer events
Follow the log while the consumer attaches to capture the first/last offsets it was compared against:
sudo journalctl -u rabbitmq-server -f | grep -iE 'stream|offset'
Step-by-Step Resolution
-
Confirm it is a range problem, not a syntax problem.
invalid offset specificationmeans fix the spec string;offset out of rangemeans the offset was valid syntax but trimmed away — checkstream_statusfor the current first offset. -
For a trimmed offset, stop pinning an absolute value. Change the consumer to a resilient spec so it never asks for a deleted offset:
# Instead of offset = 500000, attach with:
first -> oldest still-retained message
next -> only messages published after attach
last -> the last chunk, then onward
-
Prefer server-side offset tracking with a named reference so the broker, not your app, remembers where you were — and clamps safely to
firstif the stored offset was trimmed. Most stream clients expose this as a subscription/consumer name plusstore_offset. -
If you must use a timestamp, pass milliseconds since the Unix epoch and verify the value falls inside the retained window shown by
stream_status; fix any client/server clock skew first. -
If retention is trimming faster than consumers can keep up, widen it on the stream (set at declare time via policy or queue arguments):
rabbitmqctl set_policy stream-retention '^events$' \
'{"max-age":"168h"}' --apply-to queues
- Fix a syntax error by using exactly one of the accepted specs. Re-attach and confirm the consumer stays connected:
rabbitmq-streams list_stream_consumers -p /
The consumer should appear with a growing offset and no repeated subscribe rejections in the log.
Prevention
- Attach with
firstornext(or a tracked named offset) rather than hard-coded absolute offsets that retention can invalidate. - Use server-side offset tracking so reconnects resume from a committed, range-checked position instead of an application guess.
- Size retention (
max-age,max-length-bytes) for your slowest consumer’s realistic lag. - Commit offsets periodically, not only at shutdown, so a crash doesn’t lose the position and fall back to zero.
- Keep broker and client clocks in sync (NTP) when using timestamp-based offset specs.
For prompts that translate a stream_status dump into the right offset strategy, see the RabbitMQ prompt library.
Related Errors
invalid offset specification— the spec string is malformed rather than out of range; fix the value, not the retention.no_stream/stream does not exist— attaching to a stream that was never declared or was deleted.- Stream publisher confirm timeout — the publish-side counterpart when confirms stall.
Frequently Asked Questions
Why did my consumer’s offset become invalid on its own? Streams enforce retention by max age or max size, which deletes the oldest segments. If your consumer stored an absolute offset that fell into a trimmed segment, that offset no longer exists and the subscribe is rejected as out of range.
What’s the safest offset spec to attach with? Use first to replay everything still retained, next to only get new messages, or a tracked named offset so the broker resumes you safely and clamps to first if your stored position was trimmed.
How is offset out of range different from invalid offset specification? Out of range means a syntactically valid offset that the stream no longer holds. Invalid specification means the string itself isn’t one of first/last/next/absolute/timestamp — a typo or bad format.
Does consuming a stream delete its messages? No. Unlike classic queues, streams are append-only logs; messages stay until retention trims them, and many consumers can read the same stream independently at different offsets.
How do I stop this recurring after restarts? Enable server-side offset tracking with a stable consumer reference so the position survives reconnects and is range-checked by the broker. For more streaming patterns, see the RabbitMQ guides.
Fixed it? Get 500 RabbitMQ & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.