Fix Redis Streams 'ERR The ID specified in XADD is equal or smaller than the target stream top item ID'

intermediate๐Ÿ”ด Redis2026-07-07| Redis 5.0+ on Linux/macOS/Windows, any client library (redis-py, ioredis, Jedis, etc.)

Error Message

ERR The ID specified in XADD is equal or smaller than the target stream top item ID
#redis#streams#xadd

The situation

You call XADD on a stream and Redis throws back:

ERR The ID specified in XADD is equal or smaller than the target stream top item ID

This almost always means one of three things: you're explicitly passing an ID that's already been used, your system clock jumped backward, or you're replaying historical events into a live stream. All three are fixable.

Why Redis rejects the write

Stream entry IDs follow the format <milliseconds>-<sequence>, and each new entry must be strictly greater than the last. That's not a quirk โ€” it's by design. Pass an ID at or below the current top, and Redis refuses the write outright. No silent overwrites, no skipping entries.

Check the current top ID before debugging further:

# Get the last entry in the stream
XREVRANGE mystream + - COUNT 1

# Or check stream metadata
XINFO STREAM mystream

Compare the last-generated-id with the ID you're trying to insert. Equal or smaller? You've confirmed the cause.

Quick fix: stop specifying IDs manually

No hard requirement for explicit IDs? Switch to auto-generation with *. Redis assigns a unique <ms>-<seq> ID automatically, and this error disappears entirely.

# Instead of this (explicit ID โ€” dangerous):
XADD mystream 1720000000000-0 event_type login user_id 42

# Do this (auto ID โ€” safe):
XADD mystream * event_type login user_id 42

One character change. Problem gone in most cases.

If you need explicit IDs: use partial auto-increment

Sometimes you want control over the millisecond part of the ID โ€” for ordering across producers โ€” but tracking sequence numbers manually is a pain. Redis 7.0 has a middle ground: pass just the timestamp and let Redis handle the sequence.

# Redis picks the sequence number for this millisecond
XADD mystream 1720000000000-* event_type login user_id 42

The <ms>-* format lets two producers share the same millisecond timestamp without colliding. Redis bumps the sequence automatically. No manual tracking, no conflicts.

Clock skew โ€” the sneaky cause

NTP sync, VM live migration, container restart โ€” any of these can push your system clock backward. When that happens, the stream already holds IDs from the "future" relative to the corrected time. Every subsequent XADD * produces a timestamp smaller than the last entry, and writes start failing.

Diagnose it fast:

# Check what the stream thinks the last ID was
XINFO STREAM mystream
# Look at: last-generated-id

# Check current server time
TIME

If last-generated-id has a timestamp ahead of what TIME returns, clock skew is your culprit.

The fix: Redis 6.2+ handles this gracefully for auto-generated IDs. Instead of using the (now smaller) current clock time, Redis reuses the last ID's timestamp and keeps incrementing the sequence. So XADD mystream * just keeps working. Explicit IDs won't get that protection though โ€” those still fail.

On Redis < 6.2, your options are: wait for the clock to catch up naturally, or use partial auto-increment (<ms>-*) with the last known good timestamp.

Replaying historical events into an existing stream

Here's a scenario that trips up a lot of teams: you're backfilling a stream with old events โ€” say, timestamps from two weeks ago โ€” but the stream already has entries from today. You can't insert older IDs after newer ones. Redis won't allow it.

Three ways out:

  • Write to a separate stream โ€” keep mystream for live events and mystream:historical for backfill. Merge at read time with XREAD or application logic.
  • Recreate the stream in order โ€” if the stream is new enough to throw away, delete it and replay events chronologically:
DEL mystream
# Replay events oldest-first with explicit IDs
XADD mystream 1719000000000-0 event_type purchase amount 99
XADD mystream 1719000001000-0 event_type refund amount 99
XADD mystream 1720000000000-0 event_type login user_id 42
  • Shift timestamps forward โ€” for non-time-sensitive replay, bump old timestamps past the current top ID. You lose chronological accuracy, but writes go through.

Race condition between multiple producers

Multiple services generating their own explicit IDs โ€” each based on its local clock โ€” is a collision waiting to happen. Two producers on the same millisecond: whoever writes second gets the error. Even being 1ms behind can trigger it.

Switch to <ms>-* partial auto-increment (Redis 7.0+) or just use full * auto-generation. For Redis < 7.0, you can hack a producer-specific prefix into the sequence field, but it gets messy quickly. Upgrading to 7.0 is the cleaner path.

Verification

Once you've applied a fix, confirm writes are landing correctly:

# Write a test entry
XADD mystream * test ok

# Confirm it landed
XREVRANGE mystream + - COUNT 1
# Should return your new entry with a valid ID

# Check stream length increased
XLEN mystream

A successful XADD returns an ID string like "1720012345678-0", not an error. If you're using a client library, check that the returned ID is non-null and your error handler has gone quiet.

Quick reference

  • Auto ID (safest): XADD key * field value
  • Partial auto-increment (Redis 7.0+): XADD key <ms>-* field value
  • Clock skew after NTP: auto IDs handle it in Redis 6.2+; avoid explicit IDs
  • Historical replay: separate stream or replay in chronological order
  • Multi-producer collisions: use * or <ms>-*, not full explicit IDs

Related Error Notes