1.1.9 Exit Codes

Exit codes are the part of a Bazel invocation that automation can trust without parsing human-oriented error text. For basic CI, zero versus non-zero is already enough to decide pass or fail, but Bazel goes further and uses different non-zero codes to separate bad flags, interrupted runs, failed tests, and environment trouble.1,2

First identify who returned the exit code
After bazel run, the number may belong to Bazel or to the launched program.
WHO RETURNED THE STATUS?
Bazel failed before launch
Interpret the number as a Bazel status, then use the command context below.
The program was launched
The number is the program's exit code. It can be the same number as a Bazel code but mean something else.
BAZEL STATUS COMMAND MEANING
STEP 1 / SHARED BAZEL CODES
If Bazel returned it, start with shared codes
These describe Bazel itself, not a program launched by bazel run.
0
success
INPUT · 2
bad flags or environment
STOPPED · 8
command was interrupted
BUSY · 9
another Bazel command holds the lock
SYSTEM · 32, 33, 36, 37, 38, 39, 45
Bazel or its environment failed. Use the exact-code lookup.
For other Bazel codes, ask which command returned the status.
STEP 2 / COMMAND-SPECIFIC
Then use the Bazel command context
This is where build, test, run, and query differ.
BUILD
Reports build success or failure
One build-specific code
1 build failed
TEST
Separates build from test outcomes
Adds test-specific meanings
1 build failed
3 tests failed or timed out
4 no tests matched
RUN
Check whether launch happened
The source of the status matters
launched use the program's exit-code contract
not launched diagnose the Bazel build or launch failure
QUERY
May return some results and an error
Query has its own meanings
3 partial success
7 query failed
3 is the reminder: in test it means failing tests, while in query it means partial success.
First identify the source. Interpret a bazel run status as a Bazel code only when Bazel failed before launching the program. After launch, use the program's own exit-code contract.

Start With The Universal Codes

Some exit codes mean the same thing no matter which Bazel command you ran. 0 is success. 2 means the command line is wrong or the environment variables are invalid. 8 means the build was interrupted. 9 means another client held the server lock and you asked Bazel not to wait with --noblock_for_lock.2

The higher-numbered infrastructure-oriented codes cover situations where Bazel itself or its environment failed before you got a normal build result: external-environment failures, Bazel OOM, internal Bazel errors, Build Event Service publishing failures, and remote-cache eviction.2 Not every non-zero exit code means the source code is broken, so scripts should distinguish infrastructure failures from ordinary build failures.

Then Ask Which Command Returned It

bazel build keeps the contract simple: 1 means the build failed.2 bazel test is more informative. It still uses 1 when the build failed, but it uses 3 when the build succeeded and some tests failed or timed out, and 4 when testing was requested but the target pattern matched no tests.2

bazel run is special in a different way. If Bazel finishes the build successfully and the launched program exits non-zero, Bazel returns the program's exit code instead of collapsing everything into one generic Bazel failure.2

bazel query also has its own meanings. Exit code 3 means partial success: Bazel returned some results, but the result is not fully reliable, commonly because --keep_going let query continue past input errors. Exit code 7 means the query command failed.2,3

That makes exit code 3 a good reminder that the command name is part of the contract. With bazel test, 3 means "tests ran, but not all passed." With bazel query, 3 means "you got a partial answer." Whether Bazel stopped early or kept reporting more work with 1.1.5 --keep_going (-k) is a separate question from what the final status code means.

Preserve The Status Before Reading The Log

bazel test //...
status=$?

case "$status" in
  0) echo "all tests passed" ;;
  3) echo "tests failed or timed out" ;;
  4) echo "no tests matched the pattern" ;;
  32|33|36|37|38|39|45) echo "Bazel or environment problem" ;;
  *) echo "inspect Bazel output and logs" ;;
esac

This keeps the machine-readable signal separate from the human investigation step. For basic CI, non-zero is enough to fail the job.1 As soon as you want smarter retry or triage policy, keep the numeric status, then read the Bazel output, bazel info command_log, and later tools from 5.6 Debugging and 5.6.1 Exit Code Taxonomy.2

Classify First, Diagnose Second

Exit codes tell you what bucket the failure belongs to. They do not replace the actual error message. A 1 still needs human reading or the phase-based triage in 2.2.2 Reading Build Output. For first checks you can make when a familiar message appears, continue with 1.1.10 Common Error Messages.

At Level 1, treat 0 as success, distinguish build failure from test failure, and avoid retrying every non-zero result blindly. The CI-focused version of that habit comes later in 3.6.3 CI-Specific Flags & Exit Codes. Bazel may also introduce more specific non-zero codes over time, so scripts should treat non-zero as failure without assuming today's table is the final one.2

key takeaway

An exit code is Bazel's compact contract with automation. Read it in two steps: first ask whether the code is universal or command-specific, then ask whether it points to broken code, failed tests, or Bazel/environment trouble. The number classifies the failure. The log explains it.

Check your understanding · 3 questions

1.True or false: what does a non-zero Bazel exit code signal?

Choose True or False for each sentence

Every non-zero Bazel exit code means the source code has a bug.
Non-zero exit codes can represent infrastructure problems like Bazel OOM, remote-cache eviction, or environment failures.
A non-zero exit code indicates warnings about deprecated flags that will be removed in the next Bazel version.
A non-zero exit code can indicate a successful build that produced more outputs than expected.

2.A CI script runs bazel test and gets a non-zero exit code. What is the recommended first step?

Select one answer

3.True or false: how do exit codes relate to Bazel commands?

Choose True or False for each sentence

The same non-zero exit code always means the same thing regardless of which Bazel command produced it.
Some codes are universal, but each command also has its own command-specific meanings.
Exit codes are only defined for bazel build — other commands always return 0 or 1.
Each command uses a completely separate set of exit codes with no overlap.
0 of 3 answered

Footnotes

  1. FAQ — basic CI can treat non-zero build/test exit status as failure, without cleaning before every run 1 2

  2. Calling Bazel from scripts — universal and command-specific exit-code table, bazel run passthrough behavior, query partial success, command_log, and the note that future Bazel versions may add more specific non-zero codes 1 2 3 4 5 6 7 8 9

  3. Query guidebazel query --keep_going continues past missing-target and load errors when partial results are acceptable