Error reference

Every validation failure carries a stable [E_*] code. Match on the code; the message always names the offending resource and the fix. All of these fail fluffy-chainsaw scan (and everything built on it) — you fix them in code or fluffy-chainsaw.yaml, never by retrying.

Code ↔ yaml correspondence

Code Meaning Fix
E_NOT_IN_CODE fluffy-chainsaw.yaml configures a resource under databases/buckets/topics/secrets/runners that no code marker declares Remove the yaml entry, or add the marker (db.connect, storage.bucket, pubsub.topic, secret.get, cloudrun.entrypoint) in code
E_NAME_COLLISION The same name is used in two collections (e.g. a database and a bucket both named users) Rename one — names are unique across all collections

Databases & instances

Code Meaning Fix
E_DB_UNBOUND A database names no instance — placement is never inferred Set databases.<name>.instance, or a defaults.databases.instance
E_INSTANCE_UNDECLARED A database binds to an instance nothing declares Add the instance under instances: (or fix the name)
E_INSTANCE_ORPHANED A declared instance no database binds to (an idle, billed server) Bind a database to it or remove it
E_USES_UNKNOWN A runner's uses.databases lists a database code never declares Add db.connect("<name>") in code, or remove it from uses
E_DATABASE_NAME_INVALID Database name breaks Postgres rules 1–63 chars, start with a letter, then letters/digits/underscores
E_INSTANCE_NAME_INVALID Instance name breaks Cloud SQL rules 1–98 chars, start with a lowercase letter, then lowercase letters/digits/dashes

Buckets

Code Meaning Fix
E_USES_BUCKET_UNKNOWN A runner's uses.buckets names a bucket code never declares Add storage.bucket("<name>") in code, or remove the binding
E_BUCKET_NO_PREFIX A bucket binding has no prefix — runners cannot access bucket root Set prefix: <something>/ on the binding
E_BUCKET_INVALID_PREFIX The prefix doesn't end with /, or contains .. / \ Use a clean trailing-slash prefix like uploads/
E_BUCKET_NO_ACCESS A bucket binding grants nothing Set viewer: true and/or writer: true
E_BUCKET_DUPLICATE A runner binds the same bucket twice One binding per bucket per runner
E_BUCKET_NAME_INVALID Logical bucket name breaks the naming rule 3–45 chars of lowercase letters, digits, dashes, underscores
E_BUCKET_LIFECYCLE abort_incomplete_upload_days ≤ 0 Use a positive number of days
E_BUCKET_EXISTING_CONFIG Owner-only knob (public/versioning/lifecycle/location) set on an existing: true bucket Remove the knobs — you can't configure a bucket another app owns

Topics & push subscriptions

Code Meaning Fix
E_USES_TOPIC_UNKNOWN A runner publishes to a topic code never declares Add pubsub.topic("<name>") in code, or remove it from uses.publishes
E_SUB_TOPIC_UNKNOWN A subscription binds to an undeclared topic Add pubsub.topic("<name>") (or existing: true for a topic another app owns)
E_SUB_DLQ_UNKNOWN dead_letter names an undeclared topic Declare the DLQ topic — it's just a normal declared topic
E_SUB_NOT_IN_CODE A yaml subscription path has no pubsub.subscription(path, handler) in code Add the handler for that exact path
E_SUB_NOT_IN_YAML Code declares pubsub.subscription(path, handler) but no runner binds that path Add trigger: { subscriptions: [{ topic: ..., path: <path> }] } to a runner
E_SUB_PUBLIC_RUNNER A runner has both trigger.http: true and push subscriptions Move the subscriptions to their own runner without trigger.http
E_SUB_PATH_SHAPE Subscription path doesn't start with / Use an absolute path like /on-order
E_SUB_ACK_DEADLINE ack_deadline outside GCP's bounds Use 10–600 seconds
E_SUB_MAX_RETRIES max_retries outside GCP's bounds Use 5–100 (and only together with dead_letter)
E_TOPIC_NAME_INVALID Topic name breaks GCP's rule 3–255 chars, start with a letter, then letters/digits/- . _ ~ + %; must not start with goog

Schedules (cron)

Code Meaning Fix
E_SCHEDULE_NOT_IN_CODE A yaml schedule path has no cloudrun.scheduled(path, handler) in code Add the handler for that exact path
E_SCHEDULE_NOT_IN_YAML Code declares cloudrun.scheduled(path, handler) but no runner schedules that path Add trigger: { schedules: [{ path: <path>, cron: "..." }] } to a runner
E_SCHEDULE_PATH_SHAPE Schedule path doesn't start with / Use an absolute path like /jobs/daily
E_SCHEDULE_NO_CRON A schedule entry has no cron expression Add cron: "0 6 * * *"-style expression
E_TRIGGER_PATH_DUP One runner reuses a path across schedules/subscriptions (they share one router) Make every schedule/subscription path unique within the runner

Secrets

Code Meaning Fix
E_USES_SECRET_UNKNOWN Either: a runner's uses.secrets names a secret code never declares, or a code-declared secret is granted to no runner Match secret.get("<name>") in code with uses: { secrets: [<name>] } on a runner; or opt out with defaults: { secrets: { unused: true } }
E_SECRETS_NO_PROJECT A runner uses secrets but has no GCP_PROJECT env Add env: { GCP_PROJECT: <project-id> } to the runner

Auth

Code Meaning Fix
E_USES_AUTH_NO_SINGLETON A runner sets uses.auth: true but there is no top-level auth: block Add auth: { engine: firebase, providers: [...], session: { ttl: 7d } }
E_AUTH_PROVIDER_UNKNOWN auth.providers lists an unsupported provider Supported today: google, github
E_AUTH_TTL_RANGE session.ttl unparseable or outside Firebase's allowed range Use a duration like 7d, 12h, 30m, within [5m, 14d]
E_ROUTER_INVALID auth.router names a runner that doesn't exist, isn't HTTP-triggered, or doesn't set uses.auth: true The named runner must be HTTP-triggered (trigger: { http: true }) and opt into auth

Realtime

Code Meaning Fix
E_USES_REALTIME_NO_SINGLETON Either: a runner sets uses.realtime: true with no top-level realtime: block, or the block is declared and used in code but no runner opts in Add realtime: { engine: firebase } and uses: { realtime: true } on the publishing runner
E_REALTIME_NOT_IN_YAML Code calls realtime.channel() but there is no realtime: block Add realtime: { engine: firebase } and uses.realtime: true on the publishing runner
E_REALTIME_NOT_IN_CODE A realtime: block is declared but code never calls realtime.channel() Remove the block, or publish to a channel in a runner
E_ROUTER_INVALID realtime.router names a runner that doesn't exist, isn't HTTP-triggered, or doesn't set uses.realtime: true The named runner must be HTTP-triggered (trigger: { http: true }) and opt into realtime
E_REALTIME_INSTANCE_INVALID realtime.instance is not a valid RTDB instance id 1–30 chars: lowercase letters, digits, dashes; no leading/trailing dash. Instance ids are globally unique

Hosting & workspace

Code Meaning Fix
E_HOSTING_SITE_INVALID hosting: declared without a site, or the site id is invalid Set hosting.site (1–30 chars: lowercase letters, digits, dashes; globally unique — it becomes <site>.web.app), or omit the block for the project's default site
E_WORKSPACE_DOUBLE_OWNERSHIP Two children of the workspace manifest both own the same physical resource (neither marks it existing: true) Exactly one child owns a shared resource; mark it existing: true in every other child, or give each child its own name (e.g. its own hosting.site / realtime.instance)

Stacks & releases

Code Meaning Fix
E_STACK_PROJECT_COLLISION Two stacks in deploy.project bind the same GCP project One project per stack — resource names are identical across stacks and would collide in a shared project

Stack failures without a code (schema/pipeline errors — the message names the exact path):

Message contains Meaning Fix
no value for stack "X" A {stack: value} map doesn't cover every declared stack Add the missing stack's value — there is no fallback
unknown stack "X" A per-stack map names a stack not in stacks: Fix the typo or declare the stack
requires a top-level stacks: list A {stack: value} map with no stacks: declared Add stacks: [...], or flatten the map to a scalar
cannot vary per stack A per-stack map on a field that must stay scalar (deploy.stack, deploy.select, deploy.infra-root, instances.region) Use a plain scalar
per-stack maps do not nest A map inside a per-stack map's value Per-stack values are scalars
stack "X" is not declared --stack (or a workspace entry's stack:) names a stack outside stacks: Declare it or fix the flag
differ beyond instance sizing Something other than instance sizing varies between stacks Only instances.{tier, disk_size, availability_type, database_version, disk_autoresize, deletion_protection} (and CLI-side deploy.*) may differ; make everything else identical
is pinned … but releases/<stack>.images.json is missing deploy.pinned stack with no committed release manifest Run fluffy-chainsaw release --to <stack> and commit the manifest
not digest-pinned A release manifest ref lacks @sha256:… Re-run fluffy-chainsaw release — hand-edited tags are refused

Migrations

Config-time (from fluffy-chainsaw scan and everything built on it):

Code Meaning Fix
E_MIGRATIONS_NO_RUN A database declares migrations without a run command Set databases.<name>.migrations.run to your migration tool command
E_MIGRATIONS_EXISTING migrations declared on an existing: true database Remove it — a referenced database's schema belongs to the owning app

Run-time (from fluffy-chainsaw migrate and the migrate stages of local/deploy):

Code Meaning Fix
E_MIGRATE_NOTHING fluffy-chainsaw migrate ran but no database declares migrations Add databases.<name>.migrations.run
E_MIGRATE_LOCAL_DOWN --local but the compose postgres isn't running Run fluffy-chainsaw local (or --deps-only) first
E_MIGRATE_CONNECT Can't connect as the migrator Locally: the volume predates the migrations block — fluffy-chainsaw local-prune and restart. Deployed: provision hasn't run since migrations were declared
E_MIGRATE_NO_OUTPUT The stack exports no migrations entry for the database Run fluffy-chainsaw provision (or deploy) after declaring migrations
E_MIGRATE_PROXY_MISSING cloud-sql-proxy is not installed brew install cloud-sql-proxy
E_MIGRATE_PROXY The proxy didn't become ready Check your gcloud identity may impersonate dbmig-<db>@… (it must match the stack's deployerPrincipal)
E_MIGRATE_TOOL_FAILED Your run command exited non-zero Read the tool's output above the error — the failure is the migration's
E_MIGRATE_GRANT_SYNC Granting DML to the runner identities failed Usually tables the migrator doesn't own (pre-migrations boot-DDL) — transfer ownership once; see migrations.md

Failures without an E_* code