Every CLI invocation that fails surfaces three things: a stable exit code, a structured error envelope on stderr, and a request ID you can quote when reporting the issue.Documentation Index
Fetch the complete documentation index at: https://developer.bron.org/llms.txt
Use this file to discover all available pages before exploring further.
Exit codes
The CLI maps HTTP status to a small set of stable exit codes. Conditional shell logic (bron tx get $id || handle "$?") and CI gating both rely on these.
| Exit code | Meaning | Source |
|---|---|---|
0 | success | request returned 2xx |
3 | unauthorized | HTTP 401 / 403 |
4 | not found | HTTP 404 |
5 | bad request | HTTP 400 |
6 | conflict | HTTP 409 (e.g. duplicate externalId with a different body) |
7 | rate limited | HTTP 429 |
8 | server error | HTTP 5xx |
1 | other | non-API error (network, file I/O, malformed flag) |
0.x. New status codes get a new mapping; existing mappings stay put.
Error envelope
A 4xx / 5xx response prints to stderr in this shape:statusis the raw HTTP status (also reflected in the exit code).codeis a stable error-code slug (AMOUNT_BELOW_MIN,WITHDRAWAL_LIMIT_EXCEEDED,INVALID_KEY, …). Branch on this in scripts; not on the human message.idis the per-request correlation ID (headerCorrelation-Id). The Bron team can pull the exact ES log line for your call with this. Quote it when reporting issues. Programmatic surfaces — Go SDKAPIError.RequestID, MCP error payloadrequestId— expose the same value under those longer names.
Why a request ID matters
The frontend calls this the Error ID. Same value: it’s the per-request correlation ID generated server-side, attached to every log line for that request across every service it touches (public-api, the worker that picked it up, MPC signers, blockchain writer). Quoting it lets us reproduce the failure exactly:id alongside whatever request ID you generate yourself — when something goes wrong, the join across logs is trivial.
Common error codes
A small reference of the most-seen codes; the canonical list is on Errors.code | Typical cause |
|---|---|
INVALID_KEY / KEY_REVOKED | API key not registered, or kid was revoked |
INSUFFICIENT_BALANCE | Withdrawal amount exceeds the account balance |
AMOUNT_BELOW_MIN | Amount under the network’s documented minimum |
EXTERNAL_ID_CONFLICT | An externalId is being reused with a different body |
INVALID_ADDRESS / ADDRESS_NOT_WHITELISTED | Destination not on the workspace allow-list |
RATE_LIMITED | You’ve hit the per-key/per-workspace rate limit; back off |
WORKSPACE_NOT_FOUND | --workspace doesn’t match the registered key |
Idempotency contract
Everytx <type> / tx create call accepts --externalId <key>. Bron de-duplicates by (workspaceId, externalId):
- Same
externalId, same body → returns the previously-created transaction. No duplicate. Exit code0. - Same
externalId, different body → exit code6(conflict),code: EXTERNAL_ID_CONFLICT. - Without
externalId→ every retry is a brand-new transaction. Don’t do this on transaction-creation calls.
--externalId from something stable (task ID, request hash, timestamp + nonce); keep it in scope across retries.
Retry strategy
The CLI itself retries idempotent reads (GET) on transient 5xx with linear backoff up to 3 attempts; you don’t have to wrap reads in a retry loop. Writes are not retried automatically — that’s the caller’s responsibility (with --externalId so retries are safe).
If you hit 429 (RATE_LIMITED), respect the Retry-After header in the response. The CLI prints it as details.retryAfter.
Reporting an issue
When the error isn’t obviously self-inflicted (config / bad input):- Re-run with
--debugto capture envelope + ping + dial logs to stderr. - Note the
id:value from the error envelope. - File the issue at https://github.com/bronlabs/bron-cli/issues with: command (redact secrets), CLI version (
bron --version), error envelope,id,--debuglog if relevant.
