> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://docs.ampdb.io/api/mcp` to find what you need.

# Job Status

A job's status report shows how far it has come: for a materialization, which blocks each of its tables holds, how far
behind the chain they are, and how much they weigh. When the job has failed, it also shows the error. Use it to follow
one [job](/managing-amp/jobs). To follow a whole deployment, use [Dataset Status](/managing-amp/dataset-status), which
reports the same progress by dataset revision.

## Prerequisites

* Amp running, with its Admin API reachable. [Solo mode](/running-amp/solo-mode) serves it at
  `http://localhost:1610`, where `ampctl` looks by default. For another Amp, set `AMP_ADMIN_URL`, and `AMP_AUTH_TOKEN`
  if it needs a token.
* `ampctl` installed (see [Installation](/running-amp/installation)).
* The ID of the job, from [`ampctl job list`](/managing-amp/jobs#list-jobs), or from
  [`ampctl dataset events`](/managing-amp/datasets#see-a-deployments-events) for a deployment's jobs. The examples use
  job 2, the `materialize-raw` job of the [Quickstart](/get-started/quickstart)'s `acme/eth_mainnet@1.0.0`
  deployment.

## Get a job's status

:::code-group
```bash [ampctl]
ampctl job status 2
```

```bash [Admin API]
curl http://localhost:1610/jobs/2/status
```
:::

`ampctl job progress` does the same.

For a materialization, `ampctl` lists each table the job writes:

```
Job ID: 2
Idempotency Key: 23f6d8595cb00fd128311d930159118ec3f8c3a1e1f075754ddcee4ae8776889
Status: COMPLETED

Tables:
  blocks:
    Block range: {mainnet: [24000000-24000099]}
    Latest segment delay: 28214361s (47 weeks)
    Blocks extracted: 100
    Files: 1
    Size: 63.25 KiB
  logs:
    Block range: {mainnet: [24000000-24000099]}
    Latest segment delay: 28214361s (47 weeks)
    Blocks extracted: 100
    Files: 1
    Size: 2.15 MiB
  transactions:
    Block range: {mainnet: [24000000-24000099]}
    Latest segment delay: 28214361s (47 weeks)
    Blocks extracted: 100
    Files: 1
    Size: 4.08 MiB
```

This job reached its end block, and the delay is how old those blocks are. A deployment without an end block keeps
following the chain, and its delay drops to seconds once it catches up.

A report is computed when you ask for it, from the files the job has written, so asking again shows the latest
progress.

## Tables

The Admin API reports each table under `tables`, keyed by its name:

```bash
curl -s http://localhost:1610/jobs/2/status | jq .tables.blocks
```

```json
{
  "synced_ranges": [
    {
      "numbers": { "start": 24000000, "end": 24000099 },
      "network": "mainnet",
      "hash": "0x6ed4b3511195052eea47925fe5035f5e3fc528440eed62649ba82058a1b70e40",
      "prev_hash": "0xe8f20171e085c248ac4d60b65ddeb9c1bd679149cc1be9925ad58799dc380b25",
      "timestamp": 1765585559
    }
  ],
  "extracted_block_count": 100,
  "latest_segment_delay_seconds": 28214361,
  "files_count": 1,
  "total_size_bytes": 64771
}
```

| Field                          | Type            | Description                                                                                                                                                                                                               |
| ------------------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `synced_ranges`                | array           | The blocks the table holds, one range per network: from its lowest block to its highest. `ampctl` shows them as `Block range`. Empty, or `(no data)` in `ampctl`, until the job has written a file.                       |
| `synced_ranges[].numbers`      | object          | The range's first and last block numbers, `start` and `end`, both included.                                                                                                                                               |
| `synced_ranges[].network`      | string          | The network the blocks are from, like `mainnet`.                                                                                                                                                                          |
| `synced_ranges[].hash`         | string          | The hash of the range's last block.                                                                                                                                                                                       |
| `synced_ranges[].prev_hash`    | string          | The hash of the block before the range's first block: all zeros when the range starts at block 0.                                                                                                                         |
| `synced_ranges[].timestamp`    | integer or none | When the range's last block was produced, as a Unix timestamp. Absent from files written by older versions of Amp.                                                                                                        |
| `extracted_block_count`        | integer         | How many blocks the table holds. Lower than the range's span while the job is still filling gaps in it, as it does when it extracts several stretches of blocks at once.                                                  |
| `latest_segment_delay_seconds` | integer or none | How long ago the newest block the table holds was produced, in seconds: how far the table is behind the chain. With several networks, the one furthest behind. `null`, or `(unknown)` in `ampctl`, when Amp doesn't know. |
| `files_count`                  | integer         | How many Parquet files hold the table's data.                                                                                                                                                                             |
| `total_size_bytes`             | integer         | The size of those files, in bytes. `ampctl` shows it in binary units, like `63.25 KiB`.                                                                                                                                   |

A job that hasn't written anything yet shows `No tables written yet.` in `ampctl`, and an empty `tables` object.

## Garbage collection

A garbage collection job writes no tables, so its report only shows its ID, idempotency key, and status, and the error
if it failed. The Admin API leaves `tables` out. To see what garbage collection has left to delete, check its
deployment's [status](/managing-amp/dataset-status#what-a-status-covers), under `garbage_collection`. See
[Garbage Collection](/running-amp/storage/garbage-collection) for how files are deleted.

## Errors

When a job is `ERROR` or `FATAL`, its report also shows the error that stopped it. Here, the job failed because no
provider was registered for its dataset's chain:

:::terminal
```bash
ampctl job status 2
```

```
Job ID: 2
Idempotency Key: 23f6d8595cb00fd128311d930159118ec3f8c3a1e1f075754ddcee4ae8776889
Status: ERROR

✖ No provider found for kind 'evm-rpc' and network 'mainnet'

  Error Code    : PROVIDER_NOT_FOUND
  Dataset       : acme/eth_mainnet
  Attempt Index : 3
  Manifest Hash : b9e2ebd92c0752ba8a61a233f9cc5d3f4f8b601b1b789a882a5dd8ece9bca56b

No tables written yet.
```
:::

The Admin API reports it under `detail`:

```json
{
  "job_id": 2,
  "idempotency_key": "23f6d8595cb00fd128311d930159118ec3f8c3a1e1f075754ddcee4ae8776889",
  "job_status": "ERROR",
  "tables": {},
  "detail": {
    "error_code": "PROVIDER_NOT_FOUND",
    "error_context": {
      "dataset": "acme/eth_mainnet",
      "attempt_index": 3,
      "manifest_hash": "b9e2ebd92c0752ba8a61a233f9cc5d3f4f8b601b1b789a882a5dd8ece9bca56b"
    },
    "error_message": "No provider found for kind 'evm-rpc' and network 'mainnet'"
  }
}
```

| Field                                | Type             | Description                                                                                                   |
| ------------------------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------- |
| `detail.error_code`                  | string           | What went wrong, as a stable code you can match in scripts, like `PROVIDER_NOT_FOUND`.                        |
| `detail.error_message`               | string           | What went wrong, in words. Its wording may change between versions.                                           |
| `detail.error_context.attempt_index` | integer          | Which attempt failed: how many times the job has been scheduled, counting from 1 for the first time.          |
| `detail.error_context.dataset`       | string           | The dataset the job works on, as `namespace/name`.                                                            |
| `detail.error_context.manifest_hash` | string           | The manifest hash of its revision.                                                                            |
| `detail.error_context.stack_trace`   | array of strings | The errors that caused this one, from the closest to the root cause. `ampctl` shows them under `Stack Trace`. |

`error_context` may hold more fields, which depend on the error: `ampctl` shows each of them. It's absent when the
error has no context.

To fix this one, [register a provider](/managing-amp/providers) for the chain. The job is still `ERROR`, not `FATAL`,
so the controller retries it, and the next attempt finds the provider. To see when the job failed before, list its
[events](/managing-amp/jobs#see-a-jobs-events).
