Exit codes
Every command reports its outcome through the process exit code, so a script or CI job can act on it without parsing output. Test zero against non-zero rather than for 1 specifically — 1 is the failure code for almost everything, but not for all of it.
| Code | Meaning |
|---|---|
0 | The command did what you asked, or there was nothing to do. |
1 | The command did not do what you asked. The reason is printed on the terminal. |
2, 127, or your command's own code | Only from brainbase run, which reports your command's outcome rather than its own. See below. |
130 | brainbase login, interrupted with Ctrl-C — 128 + SIGINT, the conventional shell code. |
brainbase agent push --yes || exit 1
if ! brainbase skill publish --name acme/thing --skill-version 1.2.0 --yes; then
echo "publish failed" >&2
exit 1
fi
brainbase run passes its command's code through
brainbase run <command> runs your command with the secrets from .brainbase/secrets.env loaded, so the code you get back is your command's, not the CLI's. If the command dies on a signal, brainbase run re-raises it so your shell sees the real cause.
| Code | Meaning |
|---|---|
2 | No command was given — a usage error, printed to stderr. |
127 | The command was not found on PATH. |
1 | The command could not be started for some other reason. |
| anything else | Whatever your command exited with, forwarded unchanged. |
So under brainbase run, test for non-zero rather than for 1, and read 2 and 127 as the CLI's own failures rather than your command's.
"Nothing to do" still exits 0
A command that had no work to do succeeded. brainbase agent push with no local changes prints Nothing to push — local is in sync with the cloud. and exits 0; brainbase sync with nothing new to bring in does the same. These print an informational line, not an error.
Declining an interactive confirmation is the same: nothing happened because you said so, not because the command failed.
Removing something that is not there counts as nothing to do. brainbase template remove and brainbase skill remove print that they found no installation and exit 0, so running either twice does not fail the second time and a teardown script can remove unconditionally. This matches apt, npm and kubectl.
brainbase skill update <slug> on a skill that is not installed reports No skill named <slug> found. — the same wording as skill remove — but exits 1. The difference is the verb, not the message: removing an absent skill is the state you asked for, updating one is not, and the skill is still missing afterwards. Branch on the exit code rather than on the text.A precondition the command cannot meet is a failure
"This folder is not linked to any agent", "no brainbase.agent.yaml here", "no skill named that is installed to update" — the command could not start, so it did not do what you asked and the code is 1. These print as warnings rather than errors, because nothing is broken and the fix is on your side, but a script should treat them as failures. The remove verbs are the exception, for the reason given above.
| Command | Precondition |
|---|---|
agent push, agent unpack | A brainbase.agent.yaml that exists and carries an id. |
agent status, sync | The folder is linked to an agent. |
agent create | The folder does not already belong to an agent. |
orchestration push, orchestration status, orchestration add-agent | The folder is linked to an orchestration. |
orchestration pull | A link, or an orchestration id as an argument. |
orchestration create | A brainbase-orchestration.yaml, and no existing link. |
skill update | A skill installed under that name. skill remove and template remove exit 0 here instead — see above. |
Partial outcomes
Some commands do more than one thing and can partly succeed. A partial result is not a success, so these exit 1 — but the output names what did land as well as what did not, so a non-zero code does not mean nothing happened.
| Command | Partial outcome | What you see |
|---|---|---|
template publish | The template publishes, but a skill inside it does not reach the registry and stays inline. | The skills that shipped inline are named, and the run closes on PUBLISHED (PARTIAL). |
orchestration pull | Some members materialise and others do not. | How many of how many landed, the missing members by name, and a note that orchestration push will refuse until they are pulled. |
agent create | The agent is created and the folder linked, but its local content does not upload. | CREATED (NO CONTENT), with the retry command to run. |
The work that succeeded is kept, not rolled back — which is why the message tells you which command to run next. It is not always the one you just ran: after a partial agent create the retry is brainbase agent push, because the agent already exists and creating it again would be refused.
brainbase team list reports the organizations it could read and marks the ones it could not with an error field in the --json payload, writing the reason to stderr. If your pipeline needs every organization to have loaded, check that field rather than the exit code.With --json
--json changes the shape of the output, not the outcome reported. stdout stays either valid JSON or empty on failure, so piping it into a parser is safe either way, and --json never turns a failure into a success.
One command's two paths do not yet agree. agent status --json exits 1 when it cannot reach the control plane, where the human-readable path prints the error and exits 0. The --json path is the stricter of the two, so a script branching on it is already correct.
Branch on the exit code, not on the output. Where the reason lands is not uniform. Many commands put a plain message on stderr and leave stdout empty. Others exit non-zero and still print their normal payload, leaving a field in it to carry the answer — agent status --json on an unlinked folder, and whoami --json with no credential, both do this. Some answer with {"error": "…"} instead, and mcp check does either depending on whether it could run at all. Each command's own reference page documents its shape, so read the exit code first and the payload second.
# Reliable for every command: test the exit code, then look at the output.
if ! out=$(brainbase agent list --json); then
echo "agent list failed" >&2
exit 1
fi
# `agent list --json` is a bare array, so count it directly.
echo "$out" | jq 'length'
brainbase mcp check reports unreachable servers in its payload and still exits 0 — it tells you the state of your MCP servers rather than passing or failing. Read check_status and the per-server entries.BRAINBASE_NON_INTERACTIVE=1 so prompts resolve instead of waiting for input — see Environment variables.