Skip to content

Promote gates ​

Promote gates are the refusals a promotion, a build dispatch, a rollback or a release cut can return. Reach for this page when a request came back with a code and you need its meaning and its fix.

How it works ​

Every gate runs before anything is dispatched, metered or written. A refusal spends nothing and moves nothing: the channel stays where it was.

The refusals group by what they check. Request gates read the body. Channel, environment and build index gates read the project's records. Workflow gates read the mirror. Rollback and cut gates belong to those two endpoints alone. The operator gate and the quota gates read the workspace.

Access refusals are decided at the door, before any gate on this page runs. They apply to every endpoint here and sit in the last table.

Not every refusal carries a code. The refusals with no code come back with a message alone, so a client that switches on code must handle that shape too.

Reference ​

Request gates ​

CodeStatusMeaningFix
INVALID_BODY400The body does not match the endpoint's schema. details names the failed fields. GET /api/projects/{id}/releases/promotion-runs answers the same code without ids, with the message Missing dispatch ids to correlate.Send the fields the endpoint documents. For a promote, buildId is the only required field. See the API reference
INVALID_ORIGIN400The dispatch body named an origin the platform does not know. The accepted values are mcp, code, console, cli, cli-unknown, import and apiSend one of those seven, or leave origin out. A dispatch with no origin is recorded as console

Channel gates ​

CodeStatusMeaningFix
UNKNOWN_CHANNEL404The promotion names a channel this project does not declare. The message lists the declared channels, which always include stable and preview. A release cut answers the same code when it names an undeclared channelCreate the channel under Project settings, Release channels, or promote to a channel in the list. See Channels
UNKNOWN_SOURCE_CHANNEL404The promotion names a sourceChannel this project does not declare. canary is accepted as the legacy name of previewPromote from a channel in the list, or leave sourceChannel unset. A promote to preview sources from preview; any other channel sources from itself
CHANNEL_STATE_INDETERMINATE409The channel list could not be read at decision time, so the gate cannot tell whether the channel exists. A cut answers the same codeRetry

A channel is promotable one second after it is created, because this gate reads the same channel record the console writes.

Environment gates ​

CodeStatusMeaningFix
BUILD_ENV_MISMATCH409The build's stamped environment does not match the environment bound to the target channelRebuild the commit in the channel's environment, or promote to a channel bound to the build's environment
UNSTAMPED_BUILD_BLOCKED409The build carries no environment stamp and the project's environments are populated, so the gate cannot place itRebuild the commit. New builds are stamped
ENV_STATE_INDETERMINATE409The environment state could not be read at decision timeRetry. If it persists, check Environments

Build index gates ​

CodeStatusMeaningFix
UNKNOWN_BUILD404The sha is not in the build index. The platform never produced that buildBuild the commit first, or correct the buildId
REBUILD_REPLACES_LIVE_ARTIFACT409The rebuild would overwrite artifacts a live channel serves, for an environment other than the one that channel requiresRebuild with the environment the channel requires. A person in the console can overwrite anyway with Replace and rebuild, which sends force: true; a project token is refused 403 REBUILD_FORCE_NEEDS_A_PERSON for it, so its message never offers it. A production rebuild of a commit that only preview serves passes without force

Workflow gates ​

CodeStatusMeaningFix
RELEASE_WORKFLOW_NOT_FOUND404Promote, rollback and GET /api/projects/{id}/releases/promotion-runs read the project's release workflow, and the mirror carries noneReconnect the project under Project settings, GitHub integration, which restores the mirror's workflows
RELEASE_DISPATCH_FAILED500One promote run dispatches per browser, and every one failedRetry. A promote where at least one browser dispatched answers 200 and names the browsers that did not

Rollback gates ​

CodeStatusMeaningFix
NO_ROLLBACK_TARGET409The channel's latest promotion left no earlier build: a first promotion, a promotion of the build the channel already held, or a channel with no event logPromote a build by id with POST /api/projects/{id}/releases. The channel gains a target once a second, different build enters it
ROLLBACK_HISTORY_UNREADABLE503The promotion history could not be read. A timeout, a registry error, a rejected token and a non-JSON answer all read this wayRetry. If it persists, the registry is the thing to check
ROLLBACK_DISPATCH_FAILED502A target resolved and every browser's dispatch failedRetry. A run where some browsers dispatched answers 200 and names the ones that did not

Release cut gates ​

A cut bumps the manifest version in your source repository and opens a release PR, so its refusals are about that repository.

CodeStatusMeaningFix
NO_SOURCE_REPO409The project is not connected to a source repositoryConnect one under Project settings, GitHub integration
APP_NOT_INSTALLED409The extension.dev GitHub App is not installed on the source repositoryReconnect the project, which reinstalls the App
MANIFEST_NOT_FOUND409No manifest.json with a version was found. The cut looks in the project's root directory first, then in the usual locationsBump the version by hand and push, or set the root directory to the folder that holds the manifest
VERSION_INVALID400The requested version is not one to four dot separated integers from 0 to 65535, such as 0.3.0 or 1.2.3.4Send a version in that shape, or send a level of patch, minor or major instead
VERSION_NOT_AHEAD400The requested version is not greater than the one the manifest carries. The message names the current oneAsk for a higher version
VERSION_UNPARSEABLE409The manifest's current version could not be read as a versionName the version you want with version, or correct the manifest and push
SOURCE_REPO_UNREACHABLE409The source repository answered 404 through the GitHub App. Renamed, deleted and uncovered by the installation all read this wayReconnect the project under Project settings, GitHub integration

Operator gate ​

CodeStatusMeaningFix
BUILD_DISPATCH_DISABLED429An operator paused build dispatch. Every request that dispatches a run on your mirror answers this code while the pause holdsNone on the client. Wait for the operator to lift the pause

The pause holds the console Rebuild button, POST /builds/dispatch, a promote, a rollback, a release cut, a store submission from the console, the API or the CLI, a rollout raise, a submission cancel and a store credential recheck.

The lanes with nobody waiting hold too. The push webhook answers its delivery with the code DISPATCH_DISABLED and skips the build. The dispatch sweep records a kill_switch refusal. The store-status sweep returns paused. A merged release PR stays pending and builds after the pause lifts. The first build after an import is skipped with a line in the import stream, and the project builds from the Builds page once dispatch resumes.

Refusals with no code ​

Status and messageEndpoint and cause
400 No commits found on the target branch.Dispatch. The commitSha was not found, or the branch carries no commits
400 GitHub App installation not found for this project.Dispatch. No App installation is recorded. Reconnect the project
404 No build workflow found for this project.Dispatch. The mirror carries no build workflow. Reconnect the project
405 Method not allowed, with an Allow headerAny endpoint here, reached with the wrong method
500 Internal server errorAny endpoint here, when the handler threw. Retry, and read the run history before promoting again

Quota reasons ​

Every quota refusal names one of twelve reasons. Where the answer lands depends on the endpoint, so read this table with the door codes below it.

ReasonMeaningFix
monthly_capThe monthly build allowance is spentWait for the monthly reset, or raise the plan at extension.dev/pricing
burst_capToo many push builds inside a sliding 60 minute window: 12 on free, 30 on pro. Decided on the push webhook and recorded, not answeredWait. The window slides and capacity returns as older dispatches age out
kill_switchAn operator paused dispatch. This is the reason behind BUILD_DISPATCH_DISABLEDNone on the client
push_builds_exhaustedThe workspace spent its push builds for the month: 25 on free, 350 on pro. Only a git push on a branch the project builds is countedClick Rebuild in the console to run the same commit now. The count resets next month. See CI and releases
counter_unavailableThe usage counter could not be read, so the gate refuses rather than dispatches unmeteredRetry
entitlement_unavailableThe workspace's plan could not be read, so no ceiling could be establishedRetry. This is never a statement about your allowance
unattributedThe push could not be tied to a workspace, so nobody could be billed. Decided on the push webhook and recorded, never answeredNone on the client. The fault is in the platform's record of the project
fork_pull_requestThe commit sits on a pull request whose head branch lives in another repository. A build runs that code and spends your allowance, so a fork PR does not start oneRead the change, then click Rebuild in the console to run that commit yourself, or merge the PR, which builds it as a push
macos_runner_not_includedThe plan carries no Safari lane. Safari builds and App Store submissions run on a macOS runner and belong to the paid plan. A free workspace is refused before any dispatch. A build that names Safari with other browsers drops the Safari leg and builds the rest; Safari alone is refusedRaise the plan at extension.dev/pricing. Chrome, Edge and Firefox are never touched by this
macos_notarization_not_includedA macOS launcher must be signed and notarized before macOS opens it. That signing belongs to the paid plan, and a free workspace is refused before anything is signed or submitted to Apple. The promotion itself succeedsRaise the plan and promote again. Signing with your own Apple Developer account stays free
safari_submissions_exhaustedThe 15 App Store submissions a paid workspace carries in a month are spent. A submission also spends one from the monthly build allowanceWait for the month to roll, or submit to the other three stores
testing_workspaceThe workspace is marked as a testing workspace and is read-only: builds, store submissions, releases, shares and AI generation are refused. Only an operator sets or clears the markerNone on the client. No retry or reset clears it

Quota door codes ​

An endpoint with a person waiting answers at the door. The code comes in two dialects.

EndpointSpent allowanceTesting workspaceUnreadable counter
POST /builds/dispatch and the console Rebuild button429 with the reason as the code, such as monthly_cap403 testing_workspace503 counter_unavailable
Promote, rollback, cut and store submission429 BUILD_QUOTA_EXHAUSTED403 TESTING_WORKSPACE_READ_ONLY503 BUILD_COUNTER_UNAVAILABLE
Safari build or App Store submission403 macos_runner_not_included or 403 safari_submissions_exhausted503 SAFARI_ALLOWANCE_UNAVAILABLE

The push webhook answers nobody, so push_builds_exhausted, fork_pull_request, burst_cap and unattributed are recorded against the workspace and read back in the console under Usage and limits.

macos_notarization_not_included is decided while a release is being promoted, after the promotion succeeded. It never sets the response status. It is reported inside the promote response under notarization.refused, with the code MACOS_NOTARIZATION_NOT_INCLUDED, the browsers it applies to and an upgrade link. Every extension package is published, and the Linux and Windows executables are built and downloadable.

Access gates ​

CodeStatusMeaning
UNAUTHENTICATED401No session and no token
BAD_TOKEN401The token does not verify, or carries no project
INVALID_PROJECT_ID400The {id} in the path is not a project id
TOKEN_PROJECT_MISMATCH403The token names a different project
OWNER_REQUIRED403The action is owner only, and the caller is not one

A cut, a rollback and GET /api/projects/{id}/releases/promotion-runs are owner only. A request that carries a token is judged on the token and never falls back to the session. See the API reference.

When it fails ​

  • A code is not on this page. The refusal came from the build itself, not from a gate. Open the build's logs with GET /api/projects/{id}/builds/{buildId}/logs.
  • A 429 with no reason you can place. Read the code. The dispatch endpoint answers the reason itself, while promote, rollback, cut and submission answer BUILD_QUOTA_EXHAUSTED. Both mean the same spent month.
  • A promote answered 200 and a browser is missing. Read failedBrowsers in the body. Only a promote where every dispatch failed answers RELEASE_DISPATCH_FAILED.
  • A promote answered 200 and no macOS launcher shipped. Read notarization.refused in the body. The plan carries no signing, and the rest of the release went out.

Next ​