> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lazycloud.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Tasks and logs

> Follow a call live, look it up later by ID, see why it is waiting, or react to it from your own code.

Remote function calls, endpoint requests, and scheduled runs each create a
task. The task keeps the call's status, result or error, and logs after the
container stops, so you can inspect a failure or collect the result of
background work later. `.local()` calls do not create tasks.

## Live progress

`lazycloud run` and `.remote()` called from your machine show the same live
display: image build, source upload, task status, and the cloud container's
stdout and stderr, followed by a summary. A failed step keeps its recent logs on screen.
When output is redirected, build logs print without live redraws.

Progress and logs go to stderr. The CLI prints the result to stdout, and Python
returns it to the caller. `output(enabled=False)` silences the display in
Python:

```python theme={null}
from lazycloud import output

with output(enabled=False):
    result = summarize_sales.remote([1200, 3500, 800])
```

Calls made inside LazyCloud containers are quiet unless wrapped in
`with output():`. Silencing output leaves exceptions and stored logs intact.

## Lifecycle

A task is `pending` until a container picks it up, then `running`. If an
attempt fails and retries remain, the same task moves to `retry`. It ends
as `complete`, `failed`, `timeout`, `cancelled`, or `expired`.

### Why a task is pending

After five seconds pending, the CLI and dashboard show the reason: compute is
starting, the container is starting, every container is busy, the workspace is
at its compute limit, an input call has not finished, or a retry delay is
running. If "every container is busy" lasts longer than a container start, the
workload is at its `max_containers`. See [Scaling](/concepts/scaling).

Your own code receives the same reason through `lazycloud.progress()`:

```python theme={null}
import lazycloud


def report(task_id: str, pending: lazycloud.TaskPendingProgress | None) -> None:
    if pending is not None:
        print(task_id, pending.reason, pending.message)


with lazycloud.progress(report):
    result = summarize_sales.remote([1200, 3500, 800])
```

The callback receives `None` when the reason clears. For a call submitted with
`.spawn()`, read `call.task.pending_progress`.

## Look up a task from Python

```python theme={null}
handle = summarize_sales.spawn([1200, 3500, 800])
print(handle.task_id)

outcome = handle.result(wait=True, timeout_seconds=120)
print(outcome.ok, outcome.status, outcome.value, outcome.error)
```

`handle.get(timeout_seconds=120)` returns the value or raises on failure, and
`handle.logs()` reads stored logs. From another process, reconnect by ID:

```python theme={null}
from lazycloud import Task

task = Task.from_id("<task-id>")
outcome = task.wait(timeout_seconds=120)
```

`task.cancel()` cancels pending work.

## Look up a task from the CLI

```bash theme={null}
uv run lazycloud task show <task-id>
uv run lazycloud logs --task-id <task-id> --follow
```

LazyCloud captures everything your code prints. `flush=True` makes lines appear
as they happen. The [task and logs reference](/cli/tasks-and-logs) lists every
command, and the dashboard's Tasks page shows the same history.

## Callbacks

Functions, endpoints, ASGI apps, and realtime handlers accept `callback_url`.
LazyCloud calls that webhook with execution events as the task changes state,
so another system can react without polling.
