<!-- generated by build_articles.py — edit content/answers/autoindex-exit-codes.md instead -->
---
title: "What do the xerj autoindex exit codes mean?"
canonical: "https://xerj.org/answers/autoindex-exit-codes"
updated: "2026-08-23"
source: "content/answers/autoindex-exit-codes.md"
---

# The indexer exited 3 (or 4). Did it fail?

**TL;DR** — Exit 3 is a success. `xerj autoindex` exits 0 when the run completed, and 3 when it completed and recorded junk. Exit 4 means the estimate exceeded `--max-minutes` and nothing was indexed. Exit 2 is a usage error, and exit 1 is an endpoint or journal failure. Only 4 is a question.

## Agent prompt

```text
Act as a coding agent. Read https://xerj.org/llms.txt, run xerj autoindex on the folder, then branch on the process exit code before you report anything: 0 and 3 are finished runs, 4 is a decision request on stdout that you answer with --approve, 2 is a bad command line, and 1 is a real failure to retry or escalate.
```

## Commands

### Command 1

Note: Print the exit-code contract from the binary you actually installed.

```sh
xerj autoindex --help
```

### Command 2

Note: A normal run. Exit 0 means nothing was refused; exit 3 means it finished and refused something.

```sh
xerj autoindex ./corpus --url http://127.0.0.1:9200 --prefix ax --state-dir ./state --progress plain
```

### Command 3

Note: Set a low budget so the estimate can trip the decision gate and exit 4.

```sh
xerj autoindex ./corpus --url http://127.0.0.1:9200 --prefix ax --state-dir ./state --max-minutes 1 --progress plain
```

### Command 4

Note: Answer a decision request by re-running the same command with --approve.

```sh
xerj autoindex ./corpus --url http://127.0.0.1:9200 --prefix ax --state-dir ./state --approve proceed --progress plain
```

### Command 5

Note: Read the journal when the process is gone and you have no terminal line.

```sh
xerj autoindex status --url http://127.0.0.1:9200 --state-dir ./state
```

## The codes, as the binary states them

The list below is quoted from `xerj autoindex --help` on the installed binary. Run that command yourself before you write a branch on any of these numbers.

| Exit | Name in `--help` | What it means for the corpus |
| --- | --- | --- |
| 0 | complete | The run finished and refused nothing. A gate answered with `--approve cancel` also exits 0. |
| 3 | completed-with-junk | The run finished. At least one file was refused and recorded. Never fatal. |
| 4 | NEEDS A DECISION | The estimate exceeded `--max-minutes`. Nothing was indexed. A JSON decision request is on stdout. |
| 2 | usage | The command line was wrong. Nothing ran. |
| 1 | endpoint/journal failure | The node or the resume journal failed, a corpus removal was refused, or an unsafe state transition was refused. |

Read the table in that order rather than in numeric order. It is the order the help text uses, and it is the order of decreasing goodness.

## Exit 3 is a finished run

`3 completed-with-junk (junk recorded, never fatal)` is the phrase in the help text, and `never fatal` is doing the work in that sentence.

A folder of real files almost always contains something XERJ will not parse. An unknown binary, an image, a file above `--max-file-gb`. Those files are recorded in the catalog with a reason, and the rest of the folder is indexed and queryable.

An agent that treats a non-zero exit as a failure will therefore throw away a perfectly good index. Branch on the specific value.

The companion check is the refusal list, not the exit code: read `autoindex-catalog` and match each refused path to its reason. The [skipped-files page](/answers/why-autoindex-skipped-files) covers that query.

## Exit 4 is a question, and nothing was written

Phase A reads and parses every file to sniff and sample it, so it measures throughput per format family on the machine you are on. It turns that into a range for the indexing phase, and the gate compares the upper end of the range against `--max-minutes` (default 10).

If the upper end is longer and no `--approve` or `--yes` was given, **nothing is indexed**. A JSON decision request goes to stdout and the process exits 4.

You answer it by re-running the same command with `--approve proceed`, `--approve fast` or `--approve cancel`. `fast` also applies `--no-semantic --no-graph`.

A person at a terminal is prompted instead, but only when the question can actually be seen: stdin a terminal, stderr a terminal, and the progress surface on. A piped or agent-driven run is never prompted, and `--quiet` silences the prompt as well. Every un-prompted run behaves identically — the JSON request goes to stdout, which `--quiet` does not silence, and the process exits 4. The payload's `prompt_not_offered_because` names which of the three conditions was missing.

`xerj autoindex` never waits on stdin for a question it did not print.

## Exit 1 and exit 2 are narrower than "any error"

The common shorthand is that 1 means any error. The binary is more specific, and the difference matters to a retry policy.

`2 usage` is a bad command line. Nothing ran, and a retry of the same command will fail the same way.

`1` is an endpoint or journal failure, a refused corpus removal, or a refused unsafe state transition. The last two are refusals by design, not crashes: removing an indexed file is refused before any remote mutation, because its documents are already live and nothing on that path deletes them.

So a 1 is not always something to retry. Read the error line first.

## Do not read the code alone

Every run that reaches an exit, success or error, ends with one terminal line in every progress mode except `none`.

```text
xerj-done ok=true exit=3 reason=completed-with-junk wall=0.6s files=4 records=1 generation=1
```

That line carries the reason string, the wall time and the code-coverage counters `code_files`, `code_files_indexed` and `code_files_junked`. A corpus whose source files were all dropped therefore cannot print the same line as a healthy one.

Two cases print no terminal line at all. `--quiet` and `--progress none` print none by design, so poll `xerj autoindex status --state-dir <dir>` or read the exit code instead of waiting for output that never comes. A run killed by a signal cannot print one either — a missing terminal line after the process is gone means it died, not that it finished.

## What an agent should do with each code

| Exit | Corpus state | Next action |
| --- | --- | --- |
| 0 | indexed, nothing refused (or cancelled on purpose) | Read the terminal line, then query. |
| 3 | indexed, something refused | Query, then read `autoindex-catalog` for the refusals. |
| 4 | untouched | Re-run the same command with `--approve proceed`, `fast` or `cancel`. |
| 2 | untouched | Fix the command line. Do not retry unchanged. |
| 1 | partial or untouched | Read the error, check the node and the journal, then decide. |

Pair this with the four-number reconciliation on the [completeness page](/answers/check-codebase-index-is-complete). The exit code tells you how the process ended; only the counts tell you what is in the index.

## How this page was checked

The codes above were read from `xerj autoindex --help` on a built binary and cross-checked against the help string in `engine/crates/xerj-autoindex/src/cli.rs`. Both agree.

No run was executed for this page to force each code, so there is no capture here and no timing. If you want the codes on your own build, `xerj autoindex --help` prints them in one screen.

## FAQ

### What does indexer exit code 3 mean?

It means the run completed and recorded junk. `xerj autoindex --help` states it as `3 completed-with-junk (junk recorded, never fatal)`. Treat it as a success with a refusal list to read.

### The indexer exited 3 (or 4). Did it fail?

3 did not fail. 4 did not fail either, and it did not index anything: it is a decision request waiting for `--approve`. Only 1 and 2 are failures.

### Why did folder indexing stop and ask me to approve?

The measured phase-A estimate for the indexing phase was longer than `--max-minutes` (default 10). With no `--approve` or `--yes`, nothing is indexed, a JSON decision request goes to stdout and the process exits 4.

### How do I tell a failed index from a completed one with junk files?

Read the exit code, not the log volume. 0 and 3 are both finished runs; 3 additionally refused at least one file. 1 is an endpoint or journal failure and nothing about it is a junk report.

### Is exit 1 the code for any error?

No, and that shorthand is wrong. `--help` scopes 1 to an endpoint or journal failure, a refused corpus removal, or a refused unsafe state transition. A bad command line is 2.

### Does exit 0 always mean the folder was indexed?

No. Answering the decision gate with `--approve cancel` also exits 0, and that run indexes nothing on purpose. Read the terminal line or the journal before you report a corpus as searchable.

### Why did my run print no terminal line at all?

Either `--quiet` or `--progress none` was in force, which prints no terminal line, or the process was killed by a signal. A missing terminal line after the process is gone means it died, not that it finished.

### Which codes should an agent retry?

Retry nothing on 0 or 3. Re-run with `--approve` on 4. Fix the command line on 2. Investigate the endpoint and the journal on 1, because a blind retry repeats the same refusal.

## Evidence

- EXIT CODES: 0 complete (also: gate answered with --approve cancel); 3 completed-with-junk (junk recorded, never fatal); 4 NEEDS A DECISION - the estimate exceeded --max-minutes and nothing was indexed, a JSON decision request is on stdout; 2 usage; 1 endpoint/journal failure, a refused corpus removal, or a refused unsafe state transition. — `engine/crates/xerj-autoindex/src/cli.rs:370`
- Exit 4 is a code of its own; 1 is the catch-all for real failures, and the decision request is answered by re-running the same command with --approve proceed, fast or cancel. — `engine/crates/xerj-autoindex/src/cli.rs:370`
- Every run that reaches an exit ends with one terminal line in every progress mode except none, which --quiet selects; a run killed by a signal cannot print one either. — `engine/crates/xerj-autoindex/src/lib.rs:277`

## Related

- [My codebase indexer says indexed but I don't see my code. How do I check it actually finished?](/answers/check-codebase-index-is-complete)
- [How do I read autoindex progress?](/answers/read-autoindex-progress)
- [How do I estimate folder-indexing time?](/answers/estimate-autoindex-time-before-running)
- [The indexer died overnight. Do I have to start over?](/answers/resume-interrupted-autoindex-run)
- [Why would a folder search miss files that I can see on disk?](/answers/why-autoindex-skipped-files)
