§ Reference · Data and formats
Exit codes
The exit status of every pravo command, what each code means, and how --json reports an error.
On this page
| Code | Name | Meaning |
|---|---|---|
| 0 | success | The command did what it was asked. For plan --detailed-exitcode: no changes. For drift: no drift. |
| 1 | error | Any failure that has no code of its own: an unknown flag, a stray argument, a missing output, a provider or state error, an apply --json that has changes and no --auto-approve, an answer other than yes. |
| 2 | changes | plan --detailed-exitcode found changes. drift found drift. This is a report and not a failure. |
| 3 | stale | A saved plan cannot be applied: the configuration, a provider or a node changed after it was made. An apply that finds a resource different from the plan, saved or printed by the same command, stops with this code too. |
| 4 | rejected | A node is locked by a running command, or reserved by another actor: before the apply changes anything, or, with lock lost, in the middle of an apply that another command took a lock from. The lock on the recorded outputs is held by a command that did not release it in time. |
| 5 | refused | A plan policy refused the run, a policy file cannot be used, or a module does not match its lock. |
| 6 | invalid | A command that loads the specification found that it does not load or evaluate: a syntax error, a conflict, a file outside the package, a setting that is not allowed, resources that depend on each other in a circle. pravo validate also exits 6 when a provider cannot be installed. pravo schema was asked for a type of a provider that is not declared. |
In --json mode an error also prints {"code":N,"kind":"error","message":"..."} on standard output. The mismatch report of pravo mod verify --json is the exception: it prints {"ok":false,"problems":[...]} alone.
Work in an empty directory. The root is infra, and the policies are in policy.
package infra
import loc "pravo.dev/providers/hashicorp/local"
$local: loc.#Provider
hello: $local.#Resource.local_file & {filename: "hello.txt", content: "hello\n"}
// A managed resource is neither deleted nor replaced.
changes: [...{
addr: string
mode: string
action: string
if mode == "managed" && (action == "delete" || action == "replace") {
error("\(addr): \(action) is not allowed")
}
}]
0 and 2: success and changes
Without --detailed-exitcode, plan exits 0 whether or not it found changes. With the flag, it exits 2 when the plan has changes and 0 when it has none. Reads, ephemeral resources, function calls and moves are not changes.
plan, one resource to create: 2
plan, nothing to do: 0
drift, none found: 0
Drift. 1 of 1 resources changed outside pravo:
hello: create
- refresh found the real state differs from the state (drift)
drift, found: 2
A job reads the code with case:
review change.plan, then apply it
1: error
Error: output "nope" is not recorded in state (run "pravo apply")
exit=1
Error: unknown flag: --nope
exit=1
{"code":1,"kind":"error","message":"apply with --json needs -auto-approve, or apply a reviewed plan file"}
A command that takes no argument refuses one. A flag whose value is optional, such as -refresh, takes it only in the form -refresh=false:
Error: pravo plan takes no arguments, but got "stray"
exit=1
Error: pravo plan takes no arguments, but got "false"; write the value of -refresh with =, as in -refresh=false
exit=1
3: stale
A saved plan is refused when the configuration changed, the state has another lineage, a provider has another version, or a node was written after the plan was made.
Error: saved plan is stale; run "pravo plan" again:
- the configuration changed since the plan was made
exit=3
4: rejected
A node that a running command holds, or that another actor has reserved, is refused to every other actor. The refusal comes before anything changes.
Error: plan rejected: hello is reserved by ci (pravo -C infra plan --r
exit=4
Unlocked hello
5: refused
A plan that violates a policy is refused after it is printed. A policy file that cannot be used is refused before anything else happens.
violation: 5
Error: policy ../policy/missing.cue: open ../policy/missing.cue: no such file or directory
unusable policy: 5
apply without a plan file: 5
A module that does not match the lock is exit 5 as well. Add an entry to the lock file for a module that nothing loads:
- example.com/x@v0 is locked but no longer used
Error: 1 module(s) do not match the lock
exit=5
{
"ok": false,
"problems": [
"example.com/x@v0 is locked but no longer used"
]
}
exit=5
6: invalid
Every command that loads the specification exits 6 when it does not load or evaluate. The error is the same for each of them:
plan: 6
eval: 6
validate: 6
An error from the state, a provider, a lock or a plan has its own code. pravo validate starts no provider and changes nothing.
package infra
size: 11
size: 3
Error: evaluating ./infra:
size: conflicting values 3 and 11:
./infra/main.cue:3:7
./infra/main.cue:4:7
exit=6
pravo schema exits 6 for a type of a provider that is not declared:
package plain
size: 3
Error: no provider "random" for type random_pet; declare a provider struct for it, or write provider/type
exit=6
In an agent session
pravo mcp returns the exit code of the command a tool ran as structuredContent.exit_code, and sets isError when it is not 0. A refusal before a process runs has no exit_code. See Operating policies.
See also
- Run pravo in a pipeline uses each code in a job.
- Global flags, environment and exit codes lists the flags every command accepts.
- Error catalogue lists the messages behind each code.