# XAUTOCLAIM

> Auto-claim idle messages.

Use `XAUTOCLAIM` to transfer ownership of pending entries that have been idle for too long, without having to name their IDs.

Starting from `<start>`, it walks the group's pending entries list and claims for `<consumer>` every entry that has been idle at least `<min-idle-time>` milliseconds, up to `COUNT` of them. The reply has three parts: a cursor to pass as `<start>` on the next call, the claimed entries themselves (or only their IDs with `JUSTID`), and the IDs that were dropped from the pending list because they no longer exist in the stream.

This is the recommended way to recover work from a consumer that crashed or stalled: it replaces the older loop of [`XPENDING`](/redis/commands/streams/xpending) to find idle entries followed by [`XCLAIM`](/redis/commands/streams/xclaim) to take them, and it can be run repeatedly by a janitor task, since the idle threshold keeps healthy consumers' work untouched.

## Syntax

```redis
XAUTOCLAIM <key> <group> <consumer> <min-idle-time> <start> [COUNT <count>] [JUSTID]
```

## Arguments

| Argument | Required | Repeatable | Description |
| --- | --- | --- | --- |
| `<key>` | Yes | No | Redis key targeted by the command. |
| `<group>` | Yes | No | Consumer group name. |
| `<consumer>` | Yes | No | Consumer name within the group. |
| `<min-idle-time>` | Yes | No | Only claim entries that have been idle at least this many milliseconds. |
| `<start>` | Yes | No | ID to start scanning the pending list from; `0-0` starts at the beginning. |
| `COUNT <count>` | No | No | Maximum number of entries to claim. |
| `JUSTID` | No | No | Return only entry IDs, without their fields and values. |

## Response

The reply reports the result of the operation. Error replies have the same shape in RESP2 and RESP3 and are surfaced as exceptions by the SDKs below.

| Protocol | Reply |
| --- | --- |
| RESP2 | Three-element array: next ID, claimed entries or IDs, and deleted IDs |
| RESP3 | Three-element array: next ID, claimed entries or IDs, and deleted IDs |

<Note>
  Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply.
</Note>

## Examples

TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN`.

<AccordionGroup>

<Accordion title="Redis CLI" icon="terminal">

```bash
XAUTOCLAIM my-key workers worker-1 60000 0-0
```

</Accordion>

<Accordion title="@upstash/redis" icon="node-js" iconType="brands">

```ts
import { Redis } from "@upstash/redis";

const redis = Redis.fromEnv();

const result = await redis.xautoclaim(
  "mystream",
  "mygroup",
  "consumer1",
  60000,
  "0-0"
);
```

</Accordion>

<Accordion title="upstash_redis" icon="python" iconType="brands">

```python
from upstash_redis import Redis

redis = Redis.from_env()
result = redis.xautoclaim("my-key", "workers", "worker-1", 60000, "0-0")
print(result)
```

</Accordion>

<Accordion title="ioredis" icon="node-js" iconType="brands">

```ts
import Redis from "ioredis";

const redis = new Redis(process.env.REDIS_URL!);
const result = await redis.xautoclaim("my-key", "workers", "worker-1", "60000", "0-0");
console.log(result);
```

</Accordion>

<Accordion title="node-redis" icon="node-js" iconType="brands">

```ts
import { createClient } from "redis";

const client = await createClient({ url: process.env.REDIS_URL })
  .on("error", console.error)
  .connect();
const result = await client.xAutoClaim("my-key", "workers", "worker-1", 60000, "0-0");
console.log(result);
```

</Accordion>

<Accordion title="redis-py" icon="python" iconType="brands">

```python
import os
import redis

client = redis.from_url(os.environ["REDIS_URL"])
result = client.xautoclaim("my-key", "workers", "worker-1", 60000, "0-0")
print(result)
```

</Accordion>

<Accordion title="go-redis" icon="golang" iconType="brands">

```go
package main

import (
    "context"
    "fmt"
    "os"
    "time"

    "github.com/redis/go-redis/v9"
)

func main() {
    opts, err := redis.ParseURL(os.Getenv("REDIS_URL"))
    if err != nil {
        panic(err)
    }
    client := redis.NewClient(opts)
    result, _, err := client.XAutoClaim(context.Background(), &redis.XAutoClaimArgs{Stream: "my-key", Group: "workers", Consumer: "worker-1", MinIdle: time.Minute, Start: "0-0"}).Result()
    if err != nil {
        panic(err)
    }
    fmt.Println(result)
}
```

</Accordion>

<Accordion title="jedis" icon="java" iconType="brands">

```java
import java.net.URI;

import redis.clients.jedis.Jedis;

try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) {
  Object result = jedis.xautoclaim("my-key", "workers", "worker-1", 60000, new redis.clients.jedis.StreamEntryID("0-0"), redis.clients.jedis.params.XAutoClaimParams.xAutoClaimParams());
  System.out.println(result);
}
```

</Accordion>

<Accordion title="redis-rs" icon="rust" iconType="brands">

```rust
use redis::streams::StreamAutoClaimOptions;
use redis::TypedCommands;

fn main() -> redis::RedisResult<()> {
    let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set");
    let client = redis::Client::open(url)?;
    let mut connection = client.get_connection()?;

    let result = connection.xautoclaim_options(
        "my-key",
        "workers",
        "worker-1",
        60000,
        "0-0",
        StreamAutoClaimOptions::default(),
    )?;
    println!("{result:?}");
    Ok(())
}
```

</Accordion>

</AccordionGroup>
