ANSWERS · OPERATIONS: EXIT CODES

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

AUTHOR · XERJ documentation team · REVIEWED BY · XERJ engineering team · PUBLISHED · 2026-08-22 · UPDATED · 2026-08-23

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.

PASTE THIS TO YOUR AI AGENT — IT DOES THE REST 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.
RUN THIS XERJ COMMAND xerj autoindex --help Print the exit-code contract from the binary you actually installed.
RUN THIS XERJ COMMAND xerj autoindex ./corpus --url http://127.0.0.1:9200 --prefix ax --state-dir ./state --progress plain A normal run. Exit 0 means nothing was refused; exit 3 means it finished and refused something.
RUN THIS XERJ COMMAND xerj autoindex ./corpus --url http://127.0.0.1:9200 --prefix ax --state-dir ./state --max-minutes 1 --progress plain Set a low budget so the estimate can trip the decision gate and exit 4.
RUN THIS XERJ COMMAND xerj autoindex ./corpus --url http://127.0.0.1:9200 --prefix ax --state-dir ./state --approve proceed --progress plain Answer a decision request by re-running the same command with --approve.
RUN THIS XERJ COMMAND xerj autoindex status --url http://127.0.0.1:9200 --state-dir ./state Read the journal when the process is gone and you have no terminal line.

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.

ExitName in --helpWhat it means for the corpus
0completeThe run finished and refused nothing. A gate answered with --approve cancel also exits 0.
3completed-with-junkThe run finished. At least one file was refused and recorded. Never fatal.
4NEEDS A DECISIONThe estimate exceeded --max-minutes. Nothing was indexed. A JSON decision request is on stdout.
2usageThe command line was wrong. Nothing ran.
1endpoint/journal failureThe 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 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.

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

ExitCorpus stateNext action
0indexed, nothing refused (or cancelled on purpose)Read the terminal line, then query.
3indexed, something refusedQuery, then read autoindex-catalog for the refusals.
4untouchedRe-run the same command with --approve proceed, fast or cancel.
2untouchedFix the command line. Do not retry unchanged.
1partial or untouchedRead the error, check the node and the journal, then decide.

Pair this with the four-number reconciliation on the completeness page. 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

Related