Rules and error codes
Every refusal carries a code, and the code is the contract: act on the code and its details, never on the message text. aircloud check and the release pipeline report the same rule ids; a release, a release part or an activation carries a deploy code; a product host answers its end users with a data-plane code.
Rules (aircloud check, and a release refused for its grammar)
| rule | phase | message |
|---|---|---|
toml.syntax |
validate | file is not valid TOML |
grammar.unsupported |
validate | aircloud.toml declares a grammar version this platform does not support |
schema.required |
validate | a required key is missing |
schema.unknown_key |
validate | a key the grammar does not define |
schema.type |
validate | a value has the wrong type |
schema.pattern |
validate | a value does not match its grammar pattern |
schema.enum |
validate | a value is not one of the allowed values |
schema.range |
validate | a number or length is outside its allowed range |
schema.shape |
validate | a value does not satisfy a conditional or one-of rule of its kind |
layout.root_missing |
validate | aircloud.toml is missing at the repository root |
layout.unknown_entry |
validate | a file or directory inside a grammar-owned root that the layout does not define |
layout.declaration_missing |
validate | a workload directory lacks its declaration file |
layout.reserved_kind |
validate | a reserved position is not built yet |
identity.name |
validate | a directory or file name is not a valid identifier for its kind |
function.entry_missing |
validate | the function's build entry does not exist |
function.component_missing |
validate | a component listed by a function does not exist under lib/ |
function.trigger_entity_unbound |
validate | an event trigger's entity selector is set on a source or topic whose events are not about a record of an entity |
function.trigger_entity_missing |
validate | an event trigger selects an entity that is not declared or is tombstoned |
component.entry_missing |
validate | the component's build entry does not exist |
migration.entry_missing |
validate | the data migration's build entry does not exist |
container.dockerfile_missing |
validate | the container's Dockerfile does not exist |
site.static_tailwind |
validate | a site with no build step cannot use Tailwind (it needs the build) |
route.overlap |
validate | two route prefixes are equal or one is a path-segment prefix of the other |
route.target_missing |
validate | a route names a function or container that does not exist |
route.reserved_path |
validate | a route prefix lies under a platform-reserved path (/.well-known/, /_air/) |
route.reserved_target |
validate | a route targets a kind that is not built yet |
authenticate.sign_in_invalid |
validate | the [authenticate] sign_in route is not reachable without a session (inside the block, or a platform-reserved path other than the hosted sign-in) |
authenticate.site_without_site |
validate | an [authenticate] route guards site pages, but the product has no site |
authenticate.role_undeclared |
validate | a default_roles or [[authenticate.route]] roles key is absent from [authenticate] roles |
authenticate.default_not_below_admin |
validate | [authenticate] default_org_path is not strictly below admin_org_path (the first admin must be able to manage self-registered end users) |
secret.used_by_missing |
validate | a secret's used_by names a workload that does not exist |
secret.undeclared |
validate | a container references a secret config/secrets.toml does not declare for it |
secret.egress_without_function |
validate | a secret declares egress hosts but no function uses it |
env.overlay_unknown |
validate | an environment overlay names an environment that does not exist |
env.overlay_reserved |
validate | an overlay names a platform-owned environment (ci, ci-*) |
env.overlay_target_missing |
validate | an overlay configures a function or container that does not exist |
schema.relation_target_missing |
validate | a relation field targets an entity that is not declared or is tombstoned |
schema.owner_field_missing |
validate | an entity's owner names no live field of the entity |
schema.owner_field_type |
validate | an entity's owner names a field that is not of type string (it holds the owning end user's sub, one line) |
schema.owner_role_undeclared |
validate | an owner_bypass_roles key is absent from [authenticate] roles |
schema.owner_shared |
validate | an entity declares both owner and shared = true: a shared record's owner is an end user of an ancestor node, so no reader below could ever be its owner |
schema.enum_values_invalid |
validate | an enum field's value lists are inconsistent: a value both live and removed, a display name for an undeclared value, or more declared values than the Flex cap |
schema.default_invalid |
validate | a field default is not a value the field accepts: a literal its type refuses, a TOML value with no JSON equivalent, an enum default that is not a live value, or a generator that is unknown, malformed or of another type |
schema.default_not_allowed |
validate | a field that takes no default declares one: a relation, file or vector field, or the entity's owner field |
seed.entity_missing |
validate | a seed or fixture file names an entity that is not declared |
seed.field_unknown |
validate | a seed record sets a field its entity does not declare |
seed.relation_key_missing |
validate | a seed relation value names a record key the same seed set does not define |
agents.stale |
validate | AGENTS.md differs from the agent map the grammar renders; regenerate it |
generate.static_tailwind |
generate | a static site cannot use Tailwind |
generate.conflict |
generate | a path the generator would create already exists (use --force to overwrite) |
generate.site_exists |
generate | the product already has a site |
generate.reserved_kind |
generate | the generator targets a kind that is not built yet |
generate.bad_field |
generate | a field argument does not match <name>:<type>[:<arg>][:required] |
generate.bad_option |
generate | an option value or combination is not allowed |
generate.bad_name |
generate | a generator's <name> is not a valid identifier for its kind (checked before anything is written) |
generate.locale_untranslated |
generate | the product declares a locale the generators have no texts for; they never write another language in its place |
destroy.referenced |
generate | another declaration still references what destroy would remove |
test.target_missing |
validate | a test/functions|components|migrations/<name>/ directory names no such workload |
test.principal_role_unknown |
validate | a test principal names a role that is not a key of [authenticate] roles |
test.principal_without_authentication |
validate | test/principals.toml declares a principal but the product declares no [authenticate] block |
test.principal_unknown |
validate | an integration step acts as a principal test/principals.toml does not declare |
test.principal_profile_undeclared |
validate | a test principal's grant names a profile that config/profiles.toml does not declare |
test.principal_grant_outside_home |
validate | a test principal's grant is placed at a node that is not at or below the principal's org node |
migration.duplicate_timestamp |
validate | two data migrations share a timestamp |
seed.environment_unknown |
validate | a db/seeds/<environment>/ directory names an environment that does not exist |
seed.environment_reserved |
validate | a db/seeds/ci/ or db/seeds/ci-*/ directory (platform-owned environments are seeded from test/fixtures/) |
seed.tombstoned |
validate | a seed record targets a tombstoned entity or sets a tombstoned field |
seed.value_type |
validate | a seed value's type is not admissible for its field type |
seed.required_missing |
validate | a seed record lacks a required field |
i18n.config_invalid |
validate | aircloud.toml [i18n] or time_zone is malformed: an unsupported locale id or zone, a default_locale outside available_locales, a fallback chain naming an unavailable locale, or a cycle |
i18n.locale_file_invalid |
validate | a locale file is not the safe YAML subset, has a top-level key other than its locale, or names a locale outside available_locales |
i18n.key_duplicate |
validate | a translation key is defined twice for one locale, or a default-locale activemodel name duplicates the display_name that carries it |
i18n.key_missing |
validate | a product translation key is missing from an available locale after its declared fallback entries |
i18n.inline_text |
validate | a site source writes user-visible text (JSX text, or a placeholder, title, aria-label, alt or label value) as a literal instead of reading it from config/locales; an aircloud-allow-inline: <reason> comment opts its line and the next out |
site.entry_invalid |
validate | a file of another kind inside a fixed-meaning app/site/src/ directory |
site.resource_unrouted |
validate | a resource page names an entity no route maps to its CRUD function |
site.layout_missing |
validate | a page or option names a layout that does not exist |
csp.policy_invalid |
validate | config/content_security_policy.toml holds a malformed directive, source expression or key, or a nonce declaration its policy conflicts with |
csp.unsafe_script |
validate | 'unsafe-inline' or 'unsafe-eval' in the effective script source list without the matching opt-in |
permissions.policy_invalid |
validate | config/permissions_policy.toml holds a malformed allowlist |
i18n.inflection_unportable |
validate | an inflection rule pattern is outside the subset every runtime reads alike (Rust regex, JavaScript iu RegExp, RE2) |
i18n.currency_unit_declared |
validate | a locale file declares number.currency.format.unit: the currency is the product's, declared once as aircloud.toml [i18n] currency, and each locale renders its CLDR symbol (ADR-255) |
i18n.currency_undeclared |
validate | code formats a currency without its own unit and aircloud.toml declares no [i18n] currency: no currency is assumed (ADR-255) |
cargo.lock_missing |
validate | a Rust crate (function, shared component or data migration) has no committed Cargo.lock beside its Cargo.toml |
layout.wit_reserved |
validate | a wit inside a function or data migration that is not the link aircloud test prepare makes: the name is reserved for the platform's guest contract, which a release never carries |
site.boot_invalid |
validate | app/site/src/main.jsx is not the boot module: side-effect imports only, the platform's CSP nonce stamp first |
schema.write_role_undeclared |
validate | a write_roles key is absent from [authenticate] roles |
route.org_path_required |
validate | a public [[route]] declares no org_path: a route with no end user acts at a declared node, never a default — write org_path = "/" to act at the tenant root |
route.org_path_not_invocation |
validate | a [[route]] that targets a container declares org_path: a container route runs no invocation, so no node acts and the platform would ignore the declaration — remove org_path |
trigger.org_path_required |
validate | an event or cron trigger declares no org_path: an invocation with no end user acts at a declared node, never a default — write org_path = "/" to act at the tenant root |
elevate.entity_missing |
validate | an [[elevate]] entity is not a live entity of db/schema |
elevate.op_invalid |
validate | an [[elevate]] op is not one of count, sum, min, max, avg, or a sum, min, max or avg has no value field its type admits |
elevate.field_identifier |
validate | an [[elevate]] filter, group_by or value field is identifying (the record id, the entity's owner field, or a unique or natural-key field): an elevated aggregate never names a record or an owner |
elevate.field_unknown |
validate | an [[elevate]] filter, group_by or value field is not a field the entity declares (the record id is elevate.field_identifier's) |
elevate.scope_invalid |
validate | an [[elevate]] scope is not node or subtree |
org.operation_unknown |
validate | an [org] operations entry is not a tree operation the capability registry publishes (contracts/capability/v1/capabilities.json) |
profile.role_undeclared |
validate | a config/profiles.toml profile names a role absent from [authenticate] roles |
workspaces.profile_undeclared |
validate | [authenticate.workspaces] admin_profile is not a profile of config/profiles.toml |
workspaces.parent_not_below_admin |
validate | [authenticate.workspaces] parent is not at or below [authenticate] admin_org_path |
workspaces.parent_nests_default |
validate | [authenticate.workspaces] parent is at or below [authenticate] default_org_path: a workspace membership would nest inside the signer's personal membership |
workspaces.parent_too_deep |
validate | [authenticate.workspaces] parent is at the org-path max_depth: no child node can be created under it |
workspaces.seed_undeclared |
validate | [authenticate.workspaces] seed names a node seed template the release does not carry (db/seeds/<environment>/nodes/<template>/) |
schema.policy_invalid |
validate | a db/schema [[policy]] rule is outside the field-policy grammar: an unknown key, effect, reference namespace, operator or combinator, a field the entity does not declare, an op its field type refuses, or a depth or size cap exceeded |
authenticate.route_at_required |
validate | an [[authenticate.route]] without at requires a role of the [authenticate.workspaces] admin_profile: a route admitting staff by such a role must be scoped to the staff's node with at |
org.path_undeclared |
validate | an org_path (a public [[route]], an event or cron trigger, an [mcp] facet) or an [[authenticate.route]] at names a node no production activation creates: it is neither the root /, a node of the [authenticate] posture (admin_org_path, default_org_path, the [authenticate.workspaces] parent, nodes) nor an ancestor of one, nor strictly below the workspaces parent, the one parent whose children are created at run time (by workspace signup, or by a function's nodes.create inside a workspace) — a node a function creates anywhere else is not declared by that code: add the node to [authenticate] nodes = [...] so each production activation creates it, or name a declared node |
Deploy codes (releases, release parts, activations and the deploy API)
| code | meaning | remedy |
|---|---|---|
source_stage_failed |
The release's git source could not be staged (the repository or ref could not be fetched and packaged). | Check the repository URL, the ref and the git credential, then create the release again. |
source_entry_refused |
The source tree carries an entry that is not a regular file or a directory (a symbolic link, a hard link, a special file). | Remove the entry from the product source. |
source_artifact_invalid |
The uploaded source artifact does not carry exactly one product source layer of the expected media type. | Upload the source with aircloud release create. |
source_too_large |
The product source exceeds the upload size limit. | Remove what the product does not need from the source. |
source_invalid |
The product source is not a readable product tarball. | Upload the source with aircloud release create. |
grammar_violation |
The product tree breaks the product grammar; the violations are listed. | Fix what aircloud check reports. |
product_slug_mismatch |
The tree declares a different product than the one the release was created for. | Create the release for the product aircloud.toml declares, or fix its slug. |
inflection_pattern_unportable |
A product inflection rule uses a pattern the platform's runtimes would read differently. | Rewrite the inflection rule with a portable pattern. |
locale_bundle_uncompilable |
The product's locale files do not compile into a locale bundle. | Fix the locale files under config/locales/. |
resource_projection_failed |
The product schema does not project into the resource projection. | Fix the schema under db/schema/. |
part_build_failed |
A part of the release failed to build; the part and its own error are named. | Fix what the part's error names. |
part_not_buildable |
A part the release scheduled has nothing to build (the release declares no such part, or no source is staged). | Create the release again; if it recurs, report it. |
render_failed |
The build job of a part could not be prepared from the release. | Create the release again; if it recurs, report it. |
aot_failed |
The ahead-of-time compile of a data migration's component failed. | Check the migration builds and runs in its unit tests, then create the release again. |
build_job_lost |
A part's build job ended without writing its result. | Create the release again; if it recurs, report it. |
part_build_cancelled |
Another part of the release failed to build, so this part's build was cancelled; the release names the part that failed. | Fix the part that failed, then create the release again. |
tenant_deprovisioned |
The product was deleted while this release work was in flight, so the work was stopped. | Nothing: the product no longer exists. |
build_failed |
A part's build failed outside a named refusal; the message carries the build's reason. | Read the message; fix the build and create the release again. |
build_input_upstream_unhealthy |
A container's build could not pull a base image: the platform's registry proxy-cache did not serve it (the message names the proxy, the upstream and the base). The platform caches the bases of every upstream it fronts, and a base it cannot serve is usually an upstream the proxy currently cannot reach, or a tag or digest the upstream does not publish. | Create the release again later; if it recurs, check that the base the message names exists upstream, pin it by digest, and report it. |
build_capacity_exhausted |
A build waited on the account's build capacity — the releases it builds at once (deploy.build.concurrent) or the parts of one release that build at once (deploy.build.parallelism) — and the builds holding that capacity ran past their own declared deadlines, so the wait ended (ADR-280). quota names the meter, the grain, the limit and its use. A build that only waits behind others that are still within their deadlines is never refused: its part shows at_capacity while it waits. | Wait for the builds already running to finish, then create the release again; to build more at once, move the account to a plan with a higher build capacity or add a build capacity add-on. |
build_capacity_unavailable |
A build waited on the platform's build host — the build slots every account shares — and the builds holding them ran past their own declared deadlines, so the wait ended (ADR-280). It is the platform's capacity, not the account's plan: no limit of the account was exceeded. | Create the release again later; if it recurs, report it. |
verification_deadline_exceeds_plan |
The verification deadline the release derives from the test layers it declares (contracts/deploy/v1/release-deadline.json: setup, plus each declared layer's base and per-item cost) exceeds what the account's plan sells for one release (the declared meter deploy.release.verification_seconds). It is refused before anything is uploaded or built (the upload's admission), or at resolve when the source was a git repository or the plan narrowed after the upload; quota names the meter, the grain, reason exceeds_limit, the limit and the deadline derived (used), both in seconds. | Declare fewer integration scenarios or system spec files (or fold several into one), or move the account to a plan with a longer verification allowance; then create the release again. |
site_bundle_missing |
At activation, the release's published site bundle is not in the registry. | Create the release again; if it recurs, report it. |
instantiation_failed |
A function of the release does not instantiate on the functions host. | Fix the function so it builds and runs in its unit tests. |
part_not_built |
At activation, a function, container, data migration or the site has no built part. | Activate a release whose build completed. |
host_label_too_long |
The product host the environment composes exceeds one DNS label. | Shorten the product slug or the environment name. |
route_table_too_large |
The product's route table exceeds what the data plane carries. | Reduce the routes in config/routes.toml. |
secret_inject_undeclared |
A secret declares egress without declaring how it is injected. | Declare the injection in config/secrets.toml. |
secret_header_undeclared |
A secret is injected as a header without naming the header. | Name the header in config/secrets.toml. |
credential_name_taken |
A registered integration already holds the secret's name in that scope. | Rename the secret, or retire the integration holding the name. |
secret_unset |
Declared secrets have no value in the environment's scope. | Set them with aircloud secrets set. |
container_build_failed |
A container of the release failed to build. | Fix the container's Dockerfile or source. |
container_deploy_failed |
A container of the release failed to deploy. | Check the container starts and serves its declared port. |
migration_failed |
A data migration refused its run; the detail carries its error. | Fix the migration. |
migration_refused |
The functions host refused a data migration run without naming a code. | Read the detail; fix the migration. |
flex.schema_refused |
The data plane refused the schema without naming a code. | Read the detail; fix the schema under db/schema/. |
flex.seed_refused |
The data plane refused a fixture record without naming a code. | Read the detail; fix the record under test/fixtures/. |
release_not_eligible |
The release cannot be activated in that environment (not built, or not verified for production). | Activate a release that is eligible there. |
release_unfunded |
The product's account cannot fund what a release of this product spends: its verification environment, the schema changes converged twice (once in verification, once on promotion), its Apps and their databases, and the fixture records verification loads. The upload is refused before its source is stored or anything is built, so the release does not fail midway through verification. The refusal lists every shortfall: for a metered action, the units the release needs and the balance the account holds; for a count limit (entities, or one entity's records), the count the release reaches and the limit. | For each metered action, add credit or move the account to a plan that includes at least the units needed; for each count limit, move the account to a plan whose limit covers the count, or declare fewer entities or fixture records. Then create the release again. |
tenant_not_active |
The account is not active, so nothing can be activated and no git credential can be registered. | Bring the account back into good standing. |
environment_not_found |
The environment does not exist. | Name an existing environment. |
activation_in_flight |
Another activation of that environment is in progress. | Wait for it to finish, then try again. |
verification_failed |
The release pipeline did not verify the release (its tests failed, or the run errored); the verdict and the cause are carried. | Read the cause; fix the tests or the product. |
auto_promotion_failed |
The verified release could not be promoted to production automatically; the detail carries why. | Promote it with aircloud release promote once the cause is resolved. |
pipeline_deadline_exceeded |
The release pipeline ran past its deadline. | Shorten the test run, then create the release again. |
activation_refused |
The pipeline could not activate the release in its test environment; the cause names the refusal. | Resolve the named refusal. |
activation_failed |
The release's activation in the pipeline's test environment failed. | Read the cause; fix what the activation reports. |
activation_abandoned |
The release left the pipeline (it concluded failed, or was superseded) before its activation ran, so the activation was ended without touching any plane. | Create a new release. |
fixtures_invalid |
The test fixtures cannot be loaded in order (their references form a cycle). | Break the cycle in test/fixtures/. |
fixtures_refused |
The data plane refused a test fixture record; the cause names its code. | Fix the record under test/fixtures/. |
build.lockfile_missing |
package.json declares dependencies and there is no package-lock.json (only npm's lockfile is accepted). | Commit package-lock.json. |
build.lockfile_entry_untrusted |
A lockfile entry has no integrity, or its integrity carries no sha512 hash (DX59: sha1 cannot anchor trust), or it resolves outside the registry (git+, file:, a link, a tarball URL, another host); or the package carries npm-shrinkwrap.json; or the lockfile is older than v2. | Regenerate the lockfile against registry.npmjs.org with npm 7 or later. |
build.integrity_mismatch |
npm ci found a tarball whose hash differs from the lockfile's integrity. | Investigate: the lockfile is the trust anchor. |
build.cargo_lock_missing |
A Rust crate (a function, shared component or data migration) has no Cargo.lock; the build compiles a crate only from its committed lock (cargo --locked). | Run cargo generate-lockfile in the crate and commit Cargo.lock. |
build.cargo_lock_entry_untrusted |
A Cargo.lock entry is neither a crate of the product (a path package) nor a crates.io package with its sha256 checksum: a git source, another registry, or a registry entry without a checksum. The detail names the entry. | Depend on crates.io releases (or on a crate of the product by path) and regenerate Cargo.lock. |
build.cargo_lock_out_of_sync |
Cargo.toml and Cargo.lock disagree: cargo --locked would have to change the lock. | Run cargo update (or cargo generate-lockfile) in the crate and commit Cargo.lock. |
build.wit_supplied |
A Rust function or data migration crate carries a wit (a directory, file or link at <crate>/wit). The guest contract a Rust crate binds is the platform's: the build places the tree of the crate's world at <crate>/wit before cargo runs, so a tree of the product's own is refused rather than replaced (ADR-281); the path names it. | Delete the named wit and create the release again. For a local build, aircloud test prepare links it to the CLI's copy of the contract, and the source-ignore rule keeps that link out of a commit and of a release. |
build.lockfile_out_of_sync |
package.json and package-lock.json disagree (npm ci refuses to install). | Run npm install and commit the lockfile. |
build.engine_unsupported |
A package's engines.node excludes the builder's node. The platform npm config sets engine-strict (DX134), so npm refuses instead of silently skipping an optional native binding; the detail names the package, the required range and the builder node. | Admit the platform's node (versions.json .node) in the package's engines.node, or pin a dependency version whose engines admit it. |
build.install_script_unreviewed |
A dependency has an install script (preinstall, install, postinstall) that the package's package.json allowScripts neither approves nor denies. The builders' npm runs with strict-allow-scripts, so npm refuses before installing anything instead of running (npm 11) or skipping (npm 12) the script with a warning; the detail names each package and its script. | Review each named script in package.json allowScripts: true runs it, false skips it (npm install-scripts approve|deny writes the entry). |
build.component_missing |
A JavaScript function lists a shared component in its function.toml components and the product has no lib/<name>/ directory for it, so the builder cannot link it where the function's imports resolve; the path is the function's function.toml and the detail names the function and the component. | Generate the component (aircloud generate component <name>) or remove it from the function's components. |
build.component_link_conflict |
A function lists a shared component whose name an installed npm package already holds in the node_modules the component must be linked into (the function's, or the test layer's), so the import would be ambiguous; the path is the function's function.toml and the detail names the component and the node_modules entry. | Rename the component, or remove the dependency of the same name from the package.json that installs it. |
build.bundle_failed |
A JavaScript function's bundle step (esbuild) reported an error, such as an import that does not resolve; the message carries the build's reason and log_tail the end of what the build printed, where esbuild's [ERROR] lines name the file and the import. | Read log_tail; fix the import or the source it names, then create the release again. |
build.compile_failed |
A Rust function's crate did not compile (cargo reported error[E…] or could not compile); the message carries the build's reason and log_tail the end of what cargo printed, with the error and its location. | Read log_tail; fix the error cargo names (cargo build reproduces it locally), then create the release again. |
build.unit_tests_failed |
The product's unit layer failed: its test:unit (run once per release in the tests part, beside every JavaScript workload package), a Rust workload crate's cargo test (in the tests part; the path names the crate), or its site unit tests (test:site, in the site part); or the product has JavaScript unit tests but no test/package.json. The refusal carries layer unit. | Fix the test or the code. |
build.site_build_failed |
The site's build command failed; the detail carries its output. | Fix the build or app/site/site.toml. |
build.site_output_missing |
The site build produced no output directory, or an empty one. | Fix the build or the output directory in app/site/site.toml. |
build.site_output_invalid |
The site's output directory escapes app/site/ or contains a symbolic link. | Keep the output inside app/site/ and free of symbolic links. |
build.site_inputs_too_large |
The release's resource projection and locale bundle exceed what a site build carries. | Reduce the declared resources or translations the site receives. |
build.test_inputs_too_large |
The release's locale bundle exceeds what the tests part carries to the unit tests. | Reduce the product's translations. |
site_publish.entry_invalid |
The built site bundle holds an entry that is not a regular file or a directory (a symbolic link, a hard link, a device). | Make the site build write regular files only. |
site_publish.reserved_path |
A site file lives under a path the platform reserves (ADR-214: public/.well-known/, public/_air/). | Move the file out of the reserved path. |
slug_invalid |
The product slug is outside the grammar (lowercase words joined by single hyphens, at most 30 characters). | Choose a slug that follows the grammar. |
slug_taken |
Another product already holds that slug. | Choose another slug. |
tenant_owner_required |
The product would have no owner. Every product has exactly one owner, a Builder Account or a platform workload, and the internal create's owner is the calling token's workload; an operator token carries none. | Create the product from a Builder Account, or call the internal create with a workload token. |
product_not_found |
No product exists for that account, or it is not visible to the caller. | Create the product, or name the right account. |
release_not_found |
The release does not exist in that product. | Name a release from aircloud release list. |
test_run_not_found |
The release has no test run in that environment. | Name an environment from the release's test_runs (aircloud release show). |
test_artifacts_not_found |
The test run pushed no artifacts (it produced no file past its report, or they exceeded the collection bound). | Read the run's report and cause on the release; a run that writes artifacts carries them. |
test_artifact_not_found |
The test run's artifacts hold no regular file at that path. | Name a path the artifacts listing returns (aircloud release artifacts). |
test_artifact_path_invalid |
The artifact path is not a clean relative path (absolute, empty segments or dot segments). | Name the path exactly as the artifacts listing returns it. |
test_artifact_too_large |
The artifact exceeds the size the platform answers for one file. | Keep test artifacts small (screenshots and traces are far under the bound). |
host_not_found |
No product serves that host. | Check the host name. |
migration_not_found |
The release declares no data migration by that name. | Name a migration the release declares. |
scope_not_found |
The secret scope is neither an environment of the product nor ci. | Name an existing environment, or ci. |
secret_not_declared |
The secret is not declared in the product's latest release. | Declare it in config/secrets.toml and create a release first. |
secret_too_large |
The secret value exceeds the size limit. | Store a smaller value. |
secret_value_invalid |
The secret value is empty or not UTF-8 text. | Set a non-empty UTF-8 value. |
secret_value_encoding_invalid |
The secret value does not decode under the key encoding its declaration names (config/secrets.toml key_encoding: base64, hex, or standard-webhooks — a whsec_-prefixed base64 secret). The value is not stored. |
Set the value exactly as the sender issued it, or declare the key_encoding that matches it (a Stripe whsec_ secret is text). |
source_digest_mismatch |
The uploaded source does not match the digest the request declared. | Upload again with aircloud release create. |
rollback_contracting |
Rolling back would cross a contracting schema change, which the data cannot undo. | Create a new release that restores the previous behaviour instead. |
rollback_target_missing |
The environment has no previous release to roll back to. | Activate a specific release instead. |
locale_bundle_not_recorded |
The release records no compiled locale bundle. | Create a new release; its resolve records the bundle. |
tenant_not_in_good_standing |
The account is not in good standing, so it cannot release or activate. | Bring the account back into good standing. |
environment_reserved |
The ci and ci-* environments are owned by the platform. | Choose another environment name. |
environment_name_invalid |
The environment name is outside the environment grammar. | Choose a name that follows the grammar. |
name_conflict |
An active integration already holds that name. | Choose another name, or retire the integration holding it. |
not_pooled |
The tenant is not on the pooled data tier (unplaced, or already dedicated), so it cannot be promoted. | Check the tenant's placement before promoting. |
git_credential_invalid |
The git credential is not usable: its username or its token is empty. | Register it again with a non-empty username and the token piped on stdin. |
git_credential_not_found |
The product holds no git credential to delete. | Nothing to delete; register one with aircloud git-credentials set when a private repository needs it. |
Data-plane codes (what a product host answers an end user)
| code | HTTP | answered by | meaning | remedy |
|---|---|---|---|---|
route_not_found |
404 | web-origin, functions-host | The host serves no product, the path is platform-reserved, no route matches, or a site asset is missing (the published 404). | |
product_unavailable |
403 | web-origin, functions-host | The host's tenant is not in good standing (ADR-220). | |
release_pin_expired |
409 | web-origin, functions-host | The request's release pin names a release the control plane no longer honours for this host; details.reload = true, and the pin cookie is cleared. | |
quota_exhausted |
429 | web-origin | A hard_block plan's deploy.site.egress_bytes allowance is spent (runtime spec § Meter and lease). | |
deploy.site.quota_unavailable |
503 | web-origin | The wallet is unreachable and this replica's one overdraft lease is spent (ADR-220 D4); Retry-After is set. | |
deploy.site.origin_unavailable |
503 | web-origin | The control-plane resolver or the bundle store could not answer — fail-closed, no host is served on a resolution the replica could not make. | |
deploy.site.upstream_unavailable |
502 | web-origin | The release-aware hop could not reach its container or route-trigger upstream. | |
deploy.site.not_acceptable |
406 | web-origin | The request's Accept-Encoding refuses every published representation of the site file, identity included (RFC 9110 §12.5.3; #645 DX72). | |
deploy.site.i18n_report_invalid |
400 | web-origin | A POST /_air/i18n-missing report is not the published {release, locale, key} body within its bounds (spec S4 § Run time). | |
session_required |
401 | web-origin | A session route was requested with no end-user session: no session cookie, or a cookie naming no session record (#632, ADR-241 decision 3). |
|
session_invalid |
401 | web-origin | A session route was requested with a session that cannot be honoured: expired, revoked (a refresh the claims target refused, or a logout), bound to another product host (cross-product), or an unsafe method whose Origin is not the product host (#632, ADR-241 decisions 2 and 3). |
|
session_role_required |
403 | web-origin | A session route narrowed to any-of roles ([[authenticate.route]] roles) was requested by an admitted end-user session whose principal holds none of them (#250, ADR-254 decision 4). A site navigation receives the 403 page and every other request this envelope; never a sign-in redirect. | |
session_membership_required |
403 | web-origin | A session route, or a selection at the membership switcher (/_air/memberships), needed a membership the authority projection grants the end user, and the session's selection names none (#384 run decision D76, ADR-293 decision 9): several memberships and none selected, a selection naming no membership of this end user (a forged or removed one), or a membership homed in an archived or unknown subtree. details.reason names which — membership_required | org_node_archived | org_node_unknown (contracts/auth/v1/authz-projection.json selection) — and details.memberships the switcher's path; a site navigation is sent to the switcher with its return_to instead. It is also the answer of a JSON POST /_air/workspaces whose new membership the projection did not carry within its propagation bound (contracts/auth/v1/account-lifecycle.json authority_projection.propagation_bound_seconds, run decision D85): the workspace exists and the selection is unchanged — select it at the switcher, never re-send the creation. Not retryable: the end user chooses. A principal the projection holds no key for at all is session_invalid (nothing to choose). |
|
session_authz_unavailable |
503 | web-origin | A session route needed the end user's authority — the selected membership, or a route's roles evaluated at its at node — and the web-origin's watch of the authority projection could not prove it is current within the projection bound (contracts/auth/v1/account-lifecycle.json), or the projection could not be read (#384, ADR-293 decision 8: fail-closed, never read as no restriction). |
|
verification_session_outside_ci |
404 | web-origin | A release verification session was asked for, or presented, outside the run it belongs to (#250, ADR-262 decisions 5 and 6): POST /_air/session/verification on a host that does not resolve to a ci- environment (refused before the control plane is asked), or a verification session presented on a host that no longer resolves to its ci- environment with its run in flight — that session record is deleted with the refusal. | |
verification_principal_refused |
403 | web-origin | A release verification token was not redeemed (#250, ADR-262 decision 5): the host's environment is not ci-, its release has no verification run in flight, or the token is unknown, already spent or expired. One answer for every reason; the control plane's internal redemption answers it too. | |
verification_session_no_platform_reach |
403 | web-origin | A release verification session reached a surface that forwards the end user's own token to a platform API, such as /_air/members (#250, ADR-262 decision 6): a verification session carries no end-user token and has no platform reach. | |
principal_assertion_invalid |
401 | functions-host | A session route reached the route trigger without a valid platform-signed principal assertion: absent, unverifiable against the published web-origin principal key, expired, or for another product host (#632, ADR-241 decision 4 — the trigger verifies before admission and fails closed). |
|
edge_rate_limited |
429 | air-edge | An Air-Edge rate limit tripped before the tunnel (#632, ADR-240 decision 3, ADR-246): the per-IP anti-abuse floor every class keeps, the per-host bucket, a class's per-IP hourly bucket, the TENANT bucket of a verified canonical token on api/cap (the tenant's plan rate), or the PRODUCT bucket of a product host (its Builder's plan rate). details.scope names the bucket that refused: per_ip | per_host | per_ip_per_hour | per_tenant | per_product. Retry-After is set. Counted and audited as an edge event, never billed to the tenant. | |
edge_request_too_large |
413 | air-edge | The request exceeded its route class's declared header-size or body-size limit, or did not deliver its body within the class's declared read-time limit, at the Air-Edge (#632, ADR-240 decision 3). | |
edge_request_refused |
403 | air-edge | The Air-Edge WAF (SecLang + OWASP CRS v4 with platform overlays) refused the request before the tunnel (#632, ADR-240 decision 3). details carries no rule text. | Resending the request unchanged is refused again: its content matched a platform WAF rule, or the WAF could not evaluate it. When it is a request your product's clients or the aircloud CLI legitimately send, report it with aircloud feedback, quoting the response's correlation_id: the edge audits every refusal by it, so the platform can name the rule the response never shows. |
edge_path_noncanonical |
403 | air-edge | The request path has more than one spelling, so the Air-Edge refused it before any route class was consulted (#250): a malformed or non-UTF-8 percent-encoding, a percent-encoded unreserved character (RFC 3986 § 2.3) or separator (/, \), an empty segment other than one trailing /, or a . / .. segment. The edge classifies every request on its ONE canonical path (edge/src/path.rs) and never rewrites one, so the origin and the edge cannot read a path differently. Sending the path in its canonical spelling succeeds. details carries no rule text. |
|
edge_policy_unavailable |
503 | air-edge | Signup or tenant-create was requested while the Air-Edge policy snapshot is stale (older than its declared maximum age) or absent: that class has no prior admission and is refused (jurisdiction 1; #632, ADR-240 decision 4 and Addendum 1). Every other class keeps the last snapshot or the declared floor; the published bound is that a principal may be served at the tier it held when the snapshot was cut for at most the snapshot's maximum age plus the alarm-to-repair time. Retry-After is set. | |
edge_host_mismatch |
421 | air-edge | The request's Host is not the name its TLS connection was accepted for (SNI), or names no host the Air-Edge serves (RFC 9110 § 15.5.20 Misdirected Request). The edge classifies and routes by that one name, so a Host switched inside a connection is refused before the tunnel rather than classified as one host and routed as another (#632, ADR-240 decision 3). Retrying on a connection opened for the Host's own name succeeds. |
CLI codes (what the aircloud CLI answers itself, before any request)
The CLI prints them as aircloud: <code>: <message> and exits 1; nothing reached the platform, so they never mean an outage.
| code | meaning | remedy |
|---|---|---|
auth.session_missing |
The command needs the Air-Cloud API and no session is stored for the identity it would act as: the product folder's account and region, else the account selected with aircloud accounts use. Nothing was sent: the CLI refuses before any network call. |
Log in to that account in that region: aircloud login --account <slug> --region <region> (the message names both), or select another logged-in account with aircloud accounts use. Automation sets AIRCLOUD_TOKEN and AIRCLOUD_API instead. |
auth.session_expired |
The stored session of the identity the command would act as has expired, and no refresh token is held to renew it silently (no OS keychain, or the login predates it). Nothing was sent: the CLI refuses before any network call. | Log in again: aircloud login --account <slug> --region <region> (the message names both). |