# Polling Async Tasks

Some operations on Stream's API take longer than a single HTTP response can wait for. Hard-delete-channels, channel export, user export, and similar batch jobs return a `task_id` immediately and run in the background. To get the result, poll the task status endpoint.

The SDK ships a helper that polls for you and surfaces the outcome as either a typed result (when the task completes) or a typed exception (when it fails or the wait elapses).

## Waiting on a task

<Tabs>

```python label="Python"
from getstream.exceptions import StreamTaskException, StreamTransportException

response = client.delete_channels(cids=["messaging:c1", "messaging:c2"], hard_delete=True)
task_id = response.task_id

try:
    result = client.wait_for_task(task_id)
    print("task completed:", result)
except StreamTaskException as e:
    print(f"task {e.task_id} failed: {e.description}")
except StreamTransportException as e:
    if e.error_type == "timeout":
        # wait elapsed; task may still be running on the server
        ...
```

```go label="Go"
import (
    "context"
    "errors"
    "github.com/GetStream/getstream-go/v4"
)

resp, err := client.Chat().DeleteChannels(ctx, &getstream.DeleteChannelsRequest{
    Cids:       []string{"messaging:c1", "messaging:c2"},
    HardDelete: getstream.PtrTo(true),
})
if err != nil {
    return err
}

taskResp, err := getstream.WaitForTask(ctx, client, *resp.Data.TaskID)
if err != nil {
    var streamErr *getstream.StreamError
    if errors.As(err, &streamErr) {
        if errors.Is(err, getstream.ErrTaskFailed) {
            // task ended with status: failed; streamErr.Task carries the details
        } else if errors.Is(err, getstream.ErrTransport) && streamErr.ErrorType == "timeout" {
            // wait elapsed; task may still be running on the server
        }
    }
}
```

```csharp label="C#"
using GetStream;

try
{
    var result = await client.WaitForTaskAsync(taskId);
    // result.Status == "completed"
}
catch (GetStreamTaskException ex)
{
    // task ended with status: failed
    Console.WriteLine($"task {ex.TaskId} failed: {ex.Description}");
}
catch (GetStreamTransportException ex) when (ex.ErrorType == "timeout")
{
    // wait elapsed; task may still be running on the server
}
```

```java label="Java"
import io.getstream.exceptions.StreamTaskException;
import io.getstream.exceptions.StreamTransportException;

try {
    var result = client.waitForTask(taskId);
    // task completed
} catch (StreamTaskException e) {
    System.err.println("task " + e.getTaskId() + " failed: " + e.getDescription());
} catch (StreamTransportException e) {
    if ("timeout".equals(e.getErrorType())) {
        // wait elapsed; task may still be running on the server
    }
}
```

```php label="PHP"
use GetStream\Exceptions\StreamTaskException;
use GetStream\Exceptions\StreamTransportException;

try {
    $result = $client->waitForTask($taskId);
    // task completed
} catch (StreamTaskException $e) {
    error_log("task {$e->getTaskId()} failed: {$e->getDescription()}");
} catch (StreamTransportException $e) {
    if ($e->getErrorType() === 'timeout') {
        // wait elapsed; task may still be running on the server
    }
}
```

```ruby label="Ruby"
begin
  result = client.wait_for_task(task_id)
  # task completed
rescue GetStreamRuby::TaskError => e
  warn "task #{e.task_id} failed: #{e.description}"
rescue GetStreamRuby::TransportError => e
  warn 'wait elapsed; task may still be running' if e.error_type == 'timeout'
end
```

</Tabs>

## Behavior

| Task outcome          | Helper's reaction                                                                                                |
| --------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `status: "completed"` | Returns the task result payload.                                                                                 |
| `status: "failed"`    | Raises the SDK's task exception with `task_id`, `error_type`, `description`, `stack_trace`, `version`.           |
| Deadline exceeded     | Raises the SDK's transport exception with `error_type = "timeout"`. The task may still be running on the server. |

<admonition type="note">

Java exposes the server-side stack trace via `getStackTraceText()`, not `getStackTrace()`. The latter is reserved for the JVM's own `Throwable.getStackTrace()` (`StackTraceElement[]`).

</admonition>

## Defaults

| Parameter     | Default    |
| ------------- | ---------- |
| Poll interval | 1 second   |
| Wait timeout  | 60 seconds |

Override either knob if your task is expected to run longer, or if you want a tighter loop.

<Tabs>

```python label="Python"
client.wait_for_task(task_id, poll_interval=5.0, timeout=600.0)
```

```go label="Go"
import "time"

getstream.WaitForTask(ctx, client, taskID,
    getstream.WithWaitForTaskPollInterval(5*time.Second),
    getstream.WithWaitForTaskTimeout(10*time.Minute),
)
```

```csharp label="C#"
await client.WaitForTaskAsync(taskId, TimeSpan.FromSeconds(5), TimeSpan.FromMinutes(10));
```

```java label="Java"
client.waitForTask(taskId, Duration.ofSeconds(5), Duration.ofMinutes(10));
```

```php label="PHP"
$client->waitForTask($taskId, 5, 600);
```

```ruby label="Ruby"
client.wait_for_task(task_id, poll_interval: 5, timeout: 600)
```

</Tabs>

## Polling manually

If you need a custom polling loop (back-off, progress logging, external cancellation), call `getTask` yourself. The helper is a convenience over the same endpoint.

<Tabs>

```python label="Python"
import time

while True:
    response = client.get_task(task_id)
    if response.data.status in ("completed", "failed"):
        break
    time.sleep(1.0)
```

```go label="Go"
for {
    resp, err := client.GetTask(ctx, taskID, &getstream.GetTaskRequest{})
    if err != nil { return err }
    if resp.Data.Status == "completed" || resp.Data.Status == "failed" {
        break
    }
    time.Sleep(time.Second)
}
```

</Tabs>

The Async variant in your SDK (e.g. Python's `AsyncStream.wait_for_task`, .NET's `WaitForTaskAsync`) is non-blocking and accepts the same parameters.


---

This page was last updated at 2026-06-01T14:18:49.768Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/java/task-polling/](https://getstream.io/chat/docs/java/task-polling/).