---
title: "Error codes"
summary: "Every typed failure code in the contract: the loader's validation codes and the CLI and engine's operation codes, all of which are reachable, and the lifecycle failure contract, where a row marked reserved is one no path raises yet."
status: shipped
generated: true
read_when:
  - "A command failed and you have a code but not a cause"
  - "Branching on a failure programmatically instead of parsing a message"
---

A code is a promise: it names a kind of failure that keeps meaning the same thing
across releases. Branch on the code, never on the sentence.

## Validation codes

Adding one is a deliberate act. A test refuses any code the loader emits that is
not enumerated, and any enumerated code that nothing emits, so this table cannot
drift in either direction.

Emitted while loading and checking the project file. Each failure carries the
code, the path that produced it, and where possible the line and the resolving
command.

| Code | Means |
| --- | --- |
| `app_required` | the shorthand form needs an application name to attach the workload to |
| `backup_driver_unsupported` | a runnable service driver has no qualified executable protection contract |
| `backup_encryption_unverified` | the selected target cannot prove the encryption mode required by the recovery kind |
| `backup_interruption_not_authorized` | the selected recovery contract needs a recurring stopped-service window the author did not permit |
| `backup_retention_unsupported` | the declared recovery history cannot map to supported retention semantics |
| `backup_target_not_independent` | a backup target shares the protected failure domain |
| `backup_target_unknown` | a protection policy selects no declared backup target |
| `compose_container_name` | a referenced service fixes its container name, which Onebox owns |
| `compose_extends` | a referenced service uses extends, which hides what runs |
| `compose_file_unparsable` | a referenced Compose file is not valid YAML |
| `compose_file_unreadable` | a referenced Compose file could not be read |
| `compose_ingress_attached` | a referenced service already attaches the ingress network |
| `compose_network_mode` | a referenced service sets network_mode, which conflicts with the network it needs |
| `compose_ob_label` | a referenced service carries a label in a namespace Onebox generates into |
| `compose_ref_malformed` | a Compose reference is not of the form path#service |
| `compose_service_missing` | a referenced Compose file has no such service |
| `compose_traefik_label` | a referenced service carries routing labels while also declaring a route |
| `connection_variable_claimed` | an authored value claims a name a managed-service connection supplies |
| `derived_name_too_long` | a name Onebox derives exceeds the runtime's limit |
| `eject_destination_exists` | the ejection destination already exists |
| `eject_failed` | the runtime could not be handed over |
| `eject_nothing_to_do` | every workload already references a Compose file |
| `env_file_missing` | an environment file the project declares is not on disk |
| `env_file_unreadable` | an environment file the project declares cannot be read |
| `health_port_unknown` | an http health check has no port to probe and none can be derived |
| `identifier_collision` | a name is used by both a workload and a service |
| `image_unresolved` | a build-sourced workload has no resolved image for this release |
| `internal_copy_failed` | a project could not be copied internally |
| `internal_decode_failed` | a normalised project could not be decoded |
| `no_environment` | a project must declare at least one environment |
| `no_workload` | a project must declare at least one workload |
| `override_invalid` | an override produced a value the contract does not accept |
| `override_not_permitted` | a field that may not vary per environment |
| `override_unknown_service` | an override names a service the project does not declare |
| `override_unknown_workload` | an override names a workload the project does not declare |
| `path_absolute` | a repository path may not be absolute |
| `path_escapes_repository` | a path resolves outside the project directory |
| `path_unresolvable` | a path could not be resolved |
| `preflight_env_incomplete` | an environment file is missing keys the project requires |
| `prerequisite_has_no_health` | a wait for health names something with no health check |
| `project_invalid` | a value that does not satisfy the contract |
| `project_unparsable` | the project file is not valid YAML, or is not a mapping |
| `project_unreadable` | the project file could not be read |
| `protected_service_patch_unsupported` | no exact qualified protected current-to-candidate image transition exists |
| `protection_image_revert_unsafe` | tag rendering would strand an effective protection prerequisite |
| `protection_service_image_unpublished` | the protected service image lacks verified publication provenance |
| `recovery_objective_unsupported` | the service driver, target, or version cannot execute the declared recovery kind |
| `render_failed` | the runtime could not be rendered |
| `restore_drill_schedule_too_sparse` | the restore-drill cadence cannot keep restore proof current |
| `route_collision` | two workloads claim the same address |
| `route_without_proxy` | a route is declared with nothing to route it |
| `routing_exclusive` | the domain shorthand and the routes list say the same thing twice |
| `routing_incomplete` | domain and port are declared together or not at all |
| `schedule_untranslatable` | a cron expression whose meaning the host's scheduler cannot preserve |
| `schema_identity_missing` | the project declares no api_version |
| `schema_identity_unsupported` | the project declares an api_version this binary does not speak |
| `secrets_withdrawn` | the withdrawn secrets block; environment files carry encrypted entries now |
| `server_unreachable` | the server could not be reached |
| `service_image_digest_unavailable` | the immutable service image is unavailable from registry and exact cache |
| `service_image_patch_disable_pending` | protected image refresh is refused while disablement is pending |
| `service_settings_unsupported` | a setting was declared for a driver with no way to apply it |
| `shorthand_and_workloads` | top-level shorthand cannot be combined with a workloads block |
| `stateful_replicas` | a workload keeping durable state asks for more than one replica |
| `strategy_ungated` | a rolling release is asked for by a workload with no health check to gate it |
| `unknown_environment` | an environment the project does not declare |
| `unknown_field` | a field the contract does not define |
| `unknown_prerequisite` | a prerequisite names something the project does not declare |
| `unknown_service_driver` | a service names a driver Onebox has no implementation for |
| `workload_malformed` | a workload is not a mapping |
| `workload_source` | a workload declares other than exactly one of build, image or compose |

## Operation codes

Raised while a command runs, rather than while the project file is read. These
are the codes a deploy, a rollback, a secret rotation or an audited exec puts in
the `error.code` field, so they are the set to branch on when automating.

A blank command is deliberate: some failures have no honest Onebox command that
resolves them. A local artifact that could not be written is fixed on the
filesystem, not by running the tool again. An angle-bracketed command names a
step to complete rather than a line to run verbatim.

| Code | Means | Guidance role | Command |
| --- | --- | --- | --- |
| `activation_refused` | the release cannot be activated from its recorded manifest state | resolving | `ob abort --output ndjson` |
| `approval_expired` | the local confirmation is older than its validity window | next | `ob approve --plan <path>` |
| `approval_failed` | the approval artifact is missing, unreadable, or not bound to this plan | next | `ob approve --plan <path>` |
| `artifact_write_failed` | a requested local artifact could not be written | — | — |
| `cancelled` | the operation was cancelled or interrupted; consult the journal for how far it got | diagnostic | `ob audit --output json` |
| `command_failed` | the command failed; diagnostic detail is on stderr | — | — |
| `compose_invalid` | the Compose project could not be loaded | diagnostic | `ob validate --output json` |
| `compose_not_found` | no Compose file was found to adopt | resolving | `ob init --output json` |
| `config_exists` | a project file already exists and init refuses to overwrite it | diagnostic | `ob validate --output json` |
| `config_write_failed` | the project file could not be written | — | — |
| `confirmation_failed` | the typed confirmation did not match the release identifier | next | `ob approve --plan <path>` |
| `divergence_detected` | the live release does not match the recorded release state | diagnostic | `ob audit --output json` |
| `doctor_failed` | a local readiness check failed | — | — |
| `exec_failed` | the audited exec could not be completed | diagnostic | `ob status --output json` |
| `finalize_refused` | the release cannot be finalized because the recorded activation evidence disagrees with the live host | diagnostic | `ob status --output json` |
| `host_owner_mismatch` | this host is owned by a different Onebox application, and one host has one owner | diagnostic | `ob preflight --output json` |
| `job_plan_failed` | the one-shot job plan could not be produced | next | `ob job plan <job> --output json` |
| `logs_failed` | log retrieval failed | diagnostic | `ob status --output json` |
| `manifest_invalid` | a release manifest is not valid closed JSON for its schema | diagnostic | `ob status --output json` |
| `manifest_missing` | a release directory carries no manifest, so its lifecycle state is unknown | diagnostic | `ob status --output json` |
| `manifest_mode_unsafe` | a release manifest is not mode 0600 on the host | diagnostic | `ob doctor --output json` |
| `manifest_read_failed` | a release manifest could not be read from the host | diagnostic | `ob status --output json` |
| `manifest_schema_unknown` | a release manifest declares a schema this binary does not support | diagnostic | `ob version --output json` |
| `manifest_write_failed` | a release manifest could not be written to the host | diagnostic | `ob status --output json` |
| `migration_backup_required` | a migration-effect step requires a plan-bound backup report or an audited override | next | `ob plan --backup-report-out <path>` |
| `migration_gate_closed` | the interrupted release ran rollback-unknown data effects, so the requested recovery action is refused | resolving | `ob resume --output ndjson` |
| `operation_failed` | the operation failed; inspect stderr and journal evidence | diagnostic | `ob audit --output json` |
| `output_mode_incompatible` | the requested output mode is incompatible with this command | diagnostic | `ob help` |
| `plan_expired` | the sealed plan is older than its validity window | next | `ob plan --output json` |
| `plan_failed` | the plan could not be produced; inspect stderr for local diagnostics | diagnostic | `ob validate --output json` |
| `plan_required` | this command requires a sealed plan when run with structured output | next | `ob plan --output json` |
| `post_activation_failed` | the release is serving, but the work after activation did not finish | resolving | `ob resume --output ndjson` |
| `preflight_failed` | a target readiness check failed before any mutation | — | — |
| `recovery_incomplete` | recovery did not reach its verified terminal state | resolving | `ob resume --output ndjson` |
| `rollback_target_missing` | no previously serving release is recorded as a rollback target | next | `ob plan --output json` |
| `secret_cleanup_pending` | the rotation is applied and verified, but removing the retired generation did not finish | resolving | `ob secrets push --output ndjson` |
| `secret_declaration_not_deployed` | the deployed release does not declare this secret graph | next | `ob plan --output json` |
| `secret_entry_not_selected` | more than one editable secret source exists, so an entry identifier is required | diagnostic | `ob secrets list --output json` |
| `secret_generation_not_deployed` | the deployed release predates opaque secret generations | next | `ob plan --output json` |
| `secret_recovery_incomplete` | secret recovery did not reach its verified terminal state | resolving | `ob secrets push --output ndjson` |
| `secret_rotation_rolled_back` | an interrupted rotation was restored to its prior generation and the requested payload was not applied | resolving | `ob secrets push --output ndjson` |
| `sops_failed` | the SOPS editor exited with a failure | — | — |
| `sops_source_missing` | a declared encrypted source file does not exist | diagnostic | `ob validate --output json` |
| `status_failed` | the status snapshot could not be read | diagnostic | `ob doctor --output json` |
| `unknown_runtime_target` | the requested runtime target is not declared | diagnostic | `ob status --output json` |

## Lifecycle failure codes

> **Belongs to the proposed protection layer**
>
> These codes are defined and drift-tested in the binary, but the operations that
> raise most of them are not yet executable. A row marked **reserved** is one no
> path raises today: the code is fixed so it stays stable when the capability
> lands, but you cannot cause it. The set is computed from the source, not
> maintained by hand.

The failure contract shared by plans, event streams, terminal results, status and
doctor. Each carries a stable code and one safe command in its semantic role; diagnostic
detail stays in restricted local evidence, never in the public record.

| Code | Reachable | Means | Guidance role | Command |
| --- | --- | --- | --- | --- |
| `assurance_stale` | reserved | continuous assurance evidence is no longer current | diagnostic | `ob status --output json` |
| `backup_conflict` | yes | another protected-service operation holds the serialization boundary | diagnostic | `ob status --output json` |
| `backup_driver_unsupported` | yes | the service driver has no qualified executable protection contract | diagnostic | `ob validate --output json` |
| `backup_encryption_unverified` | yes | the selected protection destination cannot prove its required encryption mode | diagnostic | `ob validate --output json` |
| `backup_interruption_not_authorized` | yes | the recovery contract requires a recurring stopped-service window the author did not permit | diagnostic | `ob validate --output json` |
| `backup_retention_unsupported` | yes | the declared recovery history cannot map to qualified native retention semantics | diagnostic | `ob validate --output json` |
| `backup_stale` | reserved | the latest recoverable point is older than policy permits | next | `ob plan --output json` |
| `backup_target_not_independent` | yes | the backup target shares the protected failure domain | diagnostic | `ob validate --output json` |
| `backup_target_unauthorized` | yes | the backup target credentials are unavailable, unsafe, or unauthorized | next | `ob plan --output json` |
| `backup_target_unknown` | yes | the protection policy selects no declared backup target | diagnostic | `ob validate --output json` |
| `backup_target_unreachable` | yes | the selected backup target cannot be reached | next | `ob plan --output json` |
| `disk_pressure_critical` | reserved | a relevant filesystem lacks safe headroom for a space-increasing mutation | diagnostic | `ob status --output json` |
| `drill_deferred_capacity` | reserved | a restore drill was deferred before materialization because aggregate staging headroom is insufficient | diagnostic | `ob status --output json` |
| `external_service_not_owned` | reserved | the requested lifecycle mutation targets a dependency Onebox does not own | diagnostic | `ob status --output json` |
| `external_service_state_stale` | reserved | an external-service observation changed after planning | next | `ob plan --output json` |
| `protected_service_identity_changed` | yes | a protected service name would orphan durable recovery identity | diagnostic | `ob validate --output json` |
| `protected_service_patch_incompatible` | reserved | the candidate protected service or helper cannot prove repository and runtime compatibility | diagnostic | `ob status --output json` |
| `protected_service_patch_unsupported` | yes | no exact qualified protected current-to-candidate transition exists | diagnostic | `ob status --output json` |
| `protection_disable_pending` | yes | protection removal is waiting for an authorized safe prerequisite reversal | diagnostic | `ob status --output json` |
| `protection_disablement_not_authorized` | yes | protection disablement requires a fresh local confirmation bound to current state | diagnostic | `ob status --output json` |
| `protection_disablement_overdue` | yes | protection disablement remains pending beyond its action deadline | diagnostic | `ob status --output json` |
| `protection_enablement_restart_not_authorized` | reserved | a restart-bound protection prerequisite lacks fresh local confirmation | diagnostic | `ob validate --output json` |
| `protection_image_revert_unsafe` | yes | the requested image reversion would strand an effective protection prerequisite | diagnostic | `ob status --output json` |
| `protection_image_update_overdue` | reserved | a qualified protected service image publication missed its maintenance target | diagnostic | `ob status --output json` |
| `protection_prerequisite_drifted` | reserved | a live prerequisite no longer matches the verified protection configuration | diagnostic | `ob validate --output json` |
| `protection_service_image_unpublished` | yes | no qualified immutable protection image is published for the observed service base | diagnostic | `ob status --output json` |
| `protection_service_patch_available` | reserved | a qualified exact protected service image transition is available | resolving | `ob service apply --output ndjson` |
| `protection_service_patch_required` | reserved | protection enablement requires a separate qualified same-major service patch first | resolving | `ob service apply --output ndjson` |
| `recovery_objective_unsupported` | yes | the selected driver, version, or target cannot execute the declared recovery kind | diagnostic | `ob validate --output json` |
| `replay_continuity_broken` | reserved | the native replay sequence has a gap inside the required recovery window | next | `ob plan --output json` |
| `restore_drill_schedule_too_sparse` | yes | the restore-drill cadence cannot keep restore proof current | diagnostic | `ob validate --output json` |
| `restore_state_stale` | reserved | live service, volume, or repository state changed after restore planning | diagnostic | `ob status --output json` |
| `service_image_digest_unavailable` | yes | the exact immutable service image required by recovery is unavailable | diagnostic | `ob status --output json` |
| `service_image_patch_disable_pending` | yes | service image refresh is refused while safe protection disablement is pending | diagnostic | `ob status --output json` |
| `service_major_upgrade_unsupported` | reserved | the requested service image transition crosses an unsupported major version | diagnostic | `ob status --output json` |