Skip to content

Errors and exit codes

A refusal carries a code for a program to match and a message for a person to read. Here, a pause targets a stopped sandbox on a provider that supports pause:

Terminal window
shard pause quiet-heron-3f0a
# shard: sandbox quiet-heron-3f0a is stopped: pause takes a running sandbox
echo $?
# 1

The API returns the same refusal as an HTTP status and a JSON body:

Terminal window
curl --unix-socket /var/lib/shard/shard.sock -X POST http://localhost/v0/sandboxes/quiet-heron-3f0a/pause
{"error":{"code":"sandbox_not_running","message":"sandbox quiet-heron-3f0a is stopped: pause takes a running sandbox"}}

Match on the code, never on the message. The message is for a person to read.

Code When
0 The command did what it says.
1 shard failed, or the request did. stderr holds one line: shard: <reason>.
The command’s own code shard exec exits with the code of the command it ran. shard shell exits with the code of the shell. shard run --attach exits with the last exit code of its process.
128 + n Signal n ended the command of shard exec or the process of shard run --attach, and shard reports it the way a shell does. SIGTERM gives 143 and SIGKILL gives 137.
126 or 127 The command of shard exec or shard run never started: 126 when it cannot run, 127 when it does not exist. shard shell exits 127 when the sandbox has neither bash nor sh, or when the --workdir directory does not exist.
1 or more shard exec or shard shell lost the command’s output. A lost stream never counts as a pass, even when the command exited 0.
125 shard run failed in shard itself. The separate code lets a script tell this apart from a process that exits 1.
130 Ctrl+C ended shard run before the process started, or ended shard setup, or a second Ctrl+C ended any other verb. See below.

When the command in shard exec or the process of shard run --attach fails, shard writes nothing of its own to stderr, so stderr holds only what the command wrote. If the command never started or its output was lost, shard writes one shard: line that says why.

With --attach, Ctrl+C detaches and exits 0. The process keeps running, and shard run prints the shard kill command that stops it. A Ctrl+C before the process starts exits 130. In every case the sandbox keeps running. Only shard stop ends it.

Code When
0 Setup finished, or there was nothing to do.
1 A check or a step failed, an option was refused, the confirmation was declined, or setup found a manual installation, which it does not change.
130 Ctrl+C ended setup. The steps that already ran stay in place.

shard daemon status prints the full status. It then exits 1 if any background task of the daemon is in backoff, so a script can use it as a health check.

Every error from the API has this body:

{"error":{"code":"in_use","message":"policy locked is held by sandbox quiet-heron-3f0a: remove the sandbox first","holders":["quiet-heron-3f0a"]}}
Field Description
code What a program matches on. The next section lists every code.
message The line the CLI prints after shard:. It names a sandbox by its name, or by its id when it has none.
holders Only on in_use, when sandboxes or snapshots hold the entry. It lists each sandbox by its name, or by its id when it has none. When no sandbox holds the entry, it lists the snapshots the same way.
exit_code Only on command_not_started: 127 for a command that does not exist, 126 for one that cannot run.

error is the only top-level field.

An internal error shows only what is safe to show, which can be as little as the daemon could not complete the request; the daemon log has the cause. The full cause stays in the daemon log. The host-only routes, such as the ones behind shard pull and shard image list, return the whole text.

To read the daemon log, run sudo journalctl -u shard for the systemd unit, or open /var/log/shard/daemon.log for the Mac LaunchDaemon. A daemon you started with shard daemon logs to its own terminal.

If the daemon fails after the WebSocket of a command stream such as shard exec opens, the stream reports it as the same error object on stream 5.

A later daemon may add a code, so a program must accept one it does not know.

Code Status When
invalid_request 400 The body does not decode, a field does not validate, a named secret or policy is unknown, an image reference does not parse, or a disk does not fit: the host disk has no room for it, or the image or the snapshot cannot take its bound. Also shard serve, when the request line does not parse.
body_too_large 413 A JSON body over 1 MiB. The daemon stops reading and closes the connection after it answers.
not_found 404 No sandbox, snapshot, policy, secret, image or exec has the reference, a file path does not exist in the sandbox, the sandbox forwards no such host port, or no route has the path. Also a create, start, resume or fork whose image files are no longer on the host. The message names the image by its digest, and shard pull brings it back.
sandbox_not_running 409 Exec, run, pause or fork on a sandbox that is not running, that the provider no longer holds, or whose provider process does not answer. Also a kill on a paused sandbox, and a process attach that the sandbox stops under.
sandbox_not_stopped 409 Start on a sandbox that is up. Remove without force on a sandbox that is up or paused. Snapshot create on a sandbox that is not stopped.
sandbox_not_paused 409 Resume on a sandbox that is not paused.
sandbox_live 409 Grant, ungrant, policy attach or policy detach while the sandbox is pending, running, paused or unresponsive. These verbs take a created or stopped sandbox only.
sandbox_failed 409 Any request except a get or a remove, on a sandbox whose create failed. The message carries the reason, and a remove frees the sandbox.
no_checkpoint 409 Resume on a paused sandbox that has no saved state to resume.
unsupported 409 The provider does not support the request, such as a pause on Sysbox. The message names the provider and the request.
in_use 409 Remove a policy, secret or image that sandboxes hold, remove an image that snapshots hold, or change the placeholder of a secret that sandboxes hold. holders lists them. Also a second attach to one exec, or a port forward whose host port is already bound, without holders.
name_taken 409 A create or fork whose name another sandbox has, a snapshot create whose name another snapshot has, or a run whose name a running process of the sandbox has.
exec_exited 409 Kill on an exec whose command ended.
exec_running 409 Delete on an exec whose command still runs.
exec_limit 429 An exec while the sandbox runs 32 execs, or the daemon runs 256 across all sandboxes. shard stops no running exec to make room, so try again after one exits.
no_process 404 A get, kill, logs or attach of a process name the sandbox does not have.
process_limit 409 A run while all 16 processes of the sandbox still run. A run of an ended name takes that entry’s place.
unauthorized 401 shard serve, when the request has no valid API token.
forbidden 403 shard serve, when the token is valid but its scopes do not cover the route, or when no scope reaches the route, such as an unknown path or a host-only route. Also a create that names, or a fork that copies, a secret without the secret:* scope or a policy without the policy:* scope, and a create that forwards a port without the port:write scope.
timeout 504 A start that the provider did not finish in 60 s, a stop, remove or restart whose provider status call did not answer in time, or an exec that the provider did not start in 20 s. Try again once the runtime is free.
command_not_started 422 An exec or a run whose command never started: it does not exist, it cannot run, or its interpreter does not exist. The message names the command and the reason. exit_code is 127 or 126.
internal 500 Any other failure. The daemon log has the cause. Also shard serve, with status 502, when the daemon does not answer on its socket.

On gVisor, shard remove --force works even when the runtime does not answer. It kills the runsc processes of the sandbox, finishes the removal, and returns timeout only if that kill fails too.