Exit codes and errors
Every failure the nuvelics tool can report, with its exit code, whether it is worth retrying, and what to do about it.
A command that works exits with 0. A command that fails exits with a non-zero code and reports one error
of a fixed set of kinds. The exit code tells you roughly what went wrong; the kind tells you exactly,
and retryable tells you whether to try again.
Exit codes
| Code | Meaning |
|---|---|
0 | The command succeeded. |
1 | An unexpected failure inside the tool, or an answer from the API it could not understand. |
2 | The command was malformed, or the tool could not act on the context or credential. For the context and credential kinds below (unknown-context, ambiguous-context, unauthenticated), nothing was sent. |
3 | The command was well formed, but the API refused it or failed. |
4 | The API could not be reached. |
Kinds
kind | Exit | Retry? | What it means |
|---|---|---|---|
usage | 2 | no | An unknown flag or command, a bad argument, a malformed --filter, or a missing --yes. Fix the command. |
unknown-context | 2 | no | --context names a context that does not exist. |
ambiguous-context | 2 | no | A write with no --context and no terminal. See Contexts. |
unauthenticated | 2 | no | There is no credential stored for the context. A person must sign in. |
forbidden | 3 | no | The API rejected the credential (a revoked or mistyped key, or a sign-in that can no longer be renewed), or the credential is valid but is not allowed to do this. If whoami returns it, a person must sign in again. |
not-found | 3 | no | There is no such item. |
invalid-request | 3 | no | The API rejected the request. The fields list says which fields to fix. |
conflict | 3 | yes | Someone changed the item while you were working. Run the same command again. |
rejected | 3 | no | The item's current state forbids this, for example approving a pitch that is already approved. Running it again will not succeed. |
rate-limited | 3 | yes | Too many requests. Wait, then run it again. |
spend-capped | 3 | no | Your organization's own spending cap has been reached. This is not a permissions problem. Raising the cap, or the cap's window rolling over, fixes it. See Spend and usage. |
claim-check-blocked | 3 | no | A push to your content system was held back by the output's claim check. See below. |
server-error | 3 | yes | A temporary failure on the server. |
network | 4 | yes | The API could not be reached. |
internal | 1 | no | An unexpected failure in the tool, or a malformed response. |
The set is closed: every failure is exactly one of these fifteen.
Decide with retryable, not the exit code
Exit code 3 covers errors you should retry (conflict, rate-limited, server-error) and errors you
should not (forbidden, not-found, invalid-request, rejected, spend-capped,
claim-check-blocked). Read retryable from the error, or use the table above.
The two that look alike behave differently. conflict means the item changed under you, so the same command
can succeed on a second try. rejected means a rule forbids the action in the item's present state, and it
will keep failing until the state changes.
Retry only where retryable is true. Wait between attempts, and give up after a small, fixed number of them.
Exit code 2 is never worth retrying unchanged.
Reading an error
With -o json, an error is a JSON object on standard error:
{"error":{"kind":"invalid-request","message":"...","exitCode":3,"retryable":false,
"requestId":"...","fields":[{"field":"name","message":"..."}]},
"meta":{"context":"prod","baseUrl":"https://api.nuvelics.com/api","orgId":null}}(meta is abridged.)
kind,exitCodeandretryableare as in the tables above.fieldshas one entry per field to fix when the kind isinvalid-request.requestId, when present, identifies the call. Include it when you ask for help.detailsappears only on kinds that carry structured data. Today that isclaim-check-blocked.
Claim-check refusals
nuvelics story outputs push refuses an output whose claim check is not clear, with claim-check-blocked.
Read error.details.reason, not the message text. It is one of open_findings, outdated or
never_checked, and error.details.openCount is the number of open findings.
-
outdatedornever_checked: ask for a check, wait for it, then push again.nuvelics story outputs check <story> <output> --context acme -o json nuvelics story outputs get <story> <output> -o json nuvelics story outputs push <story> <output> --context acme -o jsoncheckreturns at once with the claim check pending. Repeatgetuntil.data.claimCheck.stateis neitherpendingnorchecking, then push. If it ends asfailed, the check did not run, and that does not hold the push back: the push goes through and is recorded as published without a check. -
open_findings: stop. Checking again does not clear findings. A person must accept or fix them, so leave the output for them and pass on therequestId.
Do not retry a push unchanged after claim-check-blocked.