JSON output and errors
The --json envelope every COMA command writes, how stdout and stderr are used, and the stable error codes with their exit codes.
Every COMA command takes --json. Scripts and coding agents can rely on the shape below and on the error codes: they are part of COMA's public interface.
Streams
| Human output | With --json | |
|---|---|---|
| stdout | The result | Only the result envelope |
| stderr | Warnings, progress, logs and errors | Logs, and the error envelope on failure |
With --json, warnings are not printed to stderr; they are collected in the result's warnings array. Colour is off. Logs (--verbose, --debug, --trace) still go to stderr, so stdout always parses as one JSON document.
Success
coma machine list --json{
"apiVersion": "coma.sh/cli/v1alpha1",
"kind": "MachineList",
"requestId": "req_01M437ZZEA4TBZSA8M7XMSGSDR",
"data": [],
"warnings": [],
"meta": {
"comaVersion": "v0.1.0-rc.4-3-g07087e6"
}
}| Field | Value |
|---|---|
apiVersion | coma.sh/cli/v1alpha1, the version of this contract |
kind | The result type, for example MachineList, DoctorReport, WorkspaceStatus, PortList |
requestId | req_ followed by a ULID. The same ID appears in this invocation's log lines |
data | The result. Its shape depends on kind |
warnings | Strings; always present, empty when there are none |
meta.comaVersion | The version of the coma that wrote it |
Keys are camelCase. Byte counts are numbers of bytes.
Failure
On failure, stdout is empty and stderr holds one error envelope. The exit code follows the error code (table below).
coma machine inspect nope --json{
"apiVersion": "coma.sh/cli/v1alpha1",
"kind": "Error",
"requestId": "req_01M437ZZDJC3MGMGBZ3DS9QKJZ",
"error": {
"code": "machine_not_found",
"message": "machine \"nope\" not found",
"retryable": false,
"details": {
"hint": "run `coma machine list`",
"machine": "nope"
}
}
}| Field | Value |
|---|---|
error.code | A stable, lower snake_case code |
error.message | What failed, for people. Do not parse it |
error.retryable | true when trying again may succeed, for example when the machine could not be reached |
error.details | An object, possibly empty. hint says what to run next; other keys depend on the code, such as diagnostics for an invalid coma.yaml or suggestions for a mistyped command |
Usage errors, such as an unknown command or flag, use the same envelope with invalid_argument when --json appears anywhere before --.
Doctor commands
Commands that run checks write their report to stdout even when a check fails, then exit with 10 (doctor_failed):
coma doctorwrites only theDoctorReport.coma endpoint doctorandcoma docker-context verifyalso write adoctor_failederror envelope to stderr.verifyreports a context changed outside COMA asdocker_context_driftedinstead.
Passthrough
coma docker and coma podman run the engine's own CLI on the machine. Their output is that CLI's output, and they exit with its exit status.
Error codes
| Code | Exit | Meaning |
|---|---|---|
invalid_argument | 2 | A command, flag, argument or environment value is wrong |
invalid_config | 10 | COMA's config file cannot be read or has an invalid value |
not_found | 3 | Something COMA needs is missing, such as the coma-sync helper |
already_exists | 4 | The thing to create exists already, such as a coma.yaml without --force |
conflict | 4 | The action conflicts with the current state, such as comad already running |
permission_denied | 5 | COMA cannot read or write a local file or directory |
unauthenticated | 5 | Reserved; no current command returns it |
unsupported | 8 | The request is not supported, such as a coma-sync helper from another release |
not_implemented | 8 | Reserved; no current command returns it |
timeout | 7 | An operation ran past its time limit |
lock_timeout | 7 | Another coma process held the state lock too long |
network_unavailable | 6 | Reserved; no current command returns it |
state_corrupt | 10 | The local state database, or a record in it, is corrupt |
migration_failed | 10 | The local state database could not be upgraded |
state_schema_too_new | 10 | The state database was written by a newer coma |
doctor_failed | 10 | A check in doctor, endpoint doctor or docker-context verify failed |
interrupted | 130 | Ctrl-C or SIGTERM stopped the command |
internal | 1 | A bug in COMA, or a failure with no more specific code |
context_not_found | 3 | No COMA context with that name |
context_already_exists | 4 | A COMA context with that name exists |
confirmation_required | 2 | The action needs confirmation and there is no terminal to ask; re-run with --yes |
machine_not_found | 3 | No machine with that name |
machine_already_exists | 4 | A machine with that name exists |
machine_unreachable | 6 | COMA cannot reach the machine over SSH |
machine_auth_failed | 5 | SSH authentication to the machine failed |
machine_in_use | 4 | Contexts still use the machine; remove with --detach to clear them |
machine_target_required | 2 | No machine given, and the current context names none |
machine_discovery_failed | 6 | COMA reached the machine but could not inventory or prepare it |
machine_unsupported_os | 8 | The machine does not run Linux |
ssh_host_key_unknown | 5 | The host key was not confirmed on first use; pass --host-key |
ssh_host_key_mismatch | 5 | The machine's host key changed since it was added |
ssh_key_unavailable | 5 | An SSH private key cannot be read or parsed |
ssh_proxy_failed | 6 | The SSH configuration needs a proxy COMA does not support, such as ProxyJump |
bootstrap_plan_failed | 8 | COMA cannot plan the bootstrap, for example on a distribution without apt |
bootstrap_failed | 6 | A bootstrap step failed; run bootstrap again to resume |
bootstrap_privilege_required | 5 | A bootstrap step needs passwordless sudo on the machine |
engine_not_found | 3 | The requested engine, or any engine, is not on the machine |
engine_selection_required | 2 | The engine is ambiguous or missing; pass --engine |
engine_unavailable | 6 | The machine has an engine, but none is working |
engine_permission_denied | 5 | The SSH user is not allowed to use the engine |
endpoint_not_found | 3 | No endpoint with that name or ID |
endpoint_backend_unavailable | 6 | The engine did not answer through the endpoint |
endpoint_socket_conflict | 4 | The endpoint's socket path is in use or is not a socket |
docker_context_conflict | 4 | A Docker context with that name exists and COMA did not create it; use --replace |
docker_context_not_found | 3 | COMA manages no Docker context with that name |
docker_context_drifted | 4 | A COMA Docker context was changed outside COMA |
daemon_unavailable | 6 | comad is not running or cannot be reached or started |
local_port_in_use | 4 | A local port is taken, by another program or another workspace |
sync_mode_unsupported | 8 | The sync mode needs the Mutagen engine, and comad runs the built-in one |
workspace_manifest_not_found | 3 | No coma.yaml here or in a parent directory, or --file does not exist |
workspace_manifest_ambiguous | 2 | Both coma.yaml and coma.yml exist, or several workspaces share the name |
workspace_manifest_invalid | 2 | coma.yaml is invalid; details.diagnostics lists each problem |
workspace_api_version_unsupported | 8 | coma.yaml uses an apiVersion this COMA cannot read |
workspace_not_found | 3 | The workspace is not registered; run coma workspace up |
workspace_target_required | 2 | No machine in coma.yaml, --machine or the current context |
workspace_target_kind_unsupported | 8 | coma.yaml targets a pool or cluster |
workspace_plan_blocked | 4 | workspace up cannot run the plan, for example bidirectional sync without your opt-in |
workspace_delete_blocked | 4 | workspace delete --remote cannot remove the copy on the machine |
A code this table does not list maps to exit 1.
Refusals inside Docker errors
When COMA refuses a container request from the Docker CLI, Compose or an SDK, the refusal comes back through the engine API, so the client shows it in its own error. COMA's message names a code in parentheses, for example:
| Code | Meaning |
|---|---|
sync_bind_source_unmanaged | A bind source is outside every directory synced to this machine |
sync_bind_source_missing | A bind source is not on the machine, for example because sync ignores it |
sync_barrier_timeout | Your latest changes were not on the machine in time; the container was not created |
These are not coma exit codes: the client that made the request decides its own exit status.