# Troubleshooting

Each section starts with what you see, explains why, and says how to fix it. Messages in code blocks are what Bumpkit prints; names, numbers and SHAs in them are examples.

`bumpkit gate` exits `0` on `pass`, `1` on `needs_approval` or `fail`, and `2` when it can't decide. See [CLI Reference](/docs/cli-flags#exit-codes).

## The gate fails

**Symptom.** The gate prints a `BLOCKED:` headline and a `BLOCKED` reason, the job exits `1`, and the GitHub check concludes `failure`:

```text
BLOCKED: Adds a private key (services/billing/keys.go:3)
Fix the blocking findings below; approval can't override them.
Version: 1.4.2 → 1.5.0 (minor). Derived from Conventional Commit types without a language model.
Committed by Dana Lee
Written with Claude Code · 95% (co-author trailer in 1 of 1 commit; measured on 40 verified releases in this workspace).
  BLOCKED · Secrets block the release: Adds a private key (services/billing/keys.go:3)
      What to do: Fix the finding and push. If it is a false positive, mark it so on the release page. An owner, admin or policy admin can break glass on the check page in the dashboard if the policy allows it. https://bumpkit.dev/docs/explanations#gate-blocked-finding
```

**Why.** Something in the policy forbids the change outright. The rule tells you which. The log shows its label; `--json` shows the rule name:

| Rule | Cause |
|---|---|
| `block_on: <kind>` | A proven high-severity finding of a kind in [`block_on`](/docs/bumpkit-yml#block-on) |
| `vulnerabilities: <level>` | A new or upgraded dependency has a known advisory at or above the level |
| `hotfix.block_on: <kind>` | A hotfix carries a kind its rules forbid |
| `hotfix.backports: block` | A fix that shipped in a release tag is missing from this branch |
| `require_checks: <name>` | A required CI check didn't run, or didn't pass, on this commit. With `require_check_apps`, a run from another App or for another commit doesn't count |
| `require_check_apps` | The gate ran outside the GitHub App, so it couldn't verify which App reported the checks |
| `require_regression_evidence: block` | The tests this change can break didn't all pass in CI-attested results for this exact commit, or Bumpkit can't work out the tests for it (Beta, flag `regression_evidence`). The reason names the tests and the [state](/docs/release-checks#regression-evidence) |
| `fix_tasks: block` | A caller still uses a function or method this change renamed or changed (Beta, flag `close_the_loop`) |
| `agents.<pattern>.<limit>` | An agent went over a budget or scope in the [`agents`](/docs/bumpkit-yml#agents) section (Beta, flag `agent_budgets`) |
| `security_alerts: <level>` | Open code scanning alerts at or above the level |
| `denied` | Someone chose **Deny** in Slack, on the dashboard or with `bumpkit approvals deny` |

**Fix.** Approval can't clear a `fail`. Change the code (remove the secret, upgrade the dependency, back-port the fix), make the required check pass, or ask the person who denied it. If the finding is a false positive, report it so it can be suppressed (see [Finding feedback](/docs/release-checks#finding-feedback)). If the change must ship now, an owner, admin or policy admin can [override](/docs/release-gate#overrides) that one commit with a reason.

## The gate needs approval

**Symptom.**

```text
NEEDS APPROVAL: Needs a human approval from sam, priya
Approve the pull request; the gate re-checks automatically.
Version: 1.4.2 → 1.5.0 (minor). Derived from Conventional Commit types without a language model.
Committed by Dana Lee
Written with Claude Code · 95% (co-author trailer in 1 of 1 commit; measured on 40 verified releases in this workspace). Agent changes, and changes no person is confirmed to have written, need a person's approval.
  NEEDS APPROVAL · Agent or unverified changes to billing need approval: Changes billing code (2 files, 10 lines). Needs 1 approval from sam, priya (has 0)
```

The job exits `1`. With the GitHub App the check concludes `action_required`.

**Why.** A rule that needs a person was triggered: a [`require_approval`](/docs/bumpkit-yml#require-approval) rule, a guarded blast radius area, a missing back-port under `backports: approve`, a hotfix that carries a feature or too many files, a check too large to read in full, or `require_checks` without the GitHub App. The headline says whose approval it needs, and the end of a rule's message says how many approvals it has.

**Fix.** Ask a qualifying reviewer to approve the pull request's latest commit on GitHub, or vote in Slack or on the dashboard. The gate runs again on its own with the GitHub App. With the Action alone, the workflow must also run on `pull_request_review` (see below). See [Approvals](/docs/approvals).

## An approval didn't count

**Symptom.** Someone approved, but the gate still says `NEEDS APPROVAL`, or the reason says `(has 0)`.

**Why.** On GitHub, Bumpkit takes each reviewer's latest review (comments are skipped) and counts it only when all of these hold:

| Condition | What breaks it |
|---|---|
| The review is **Approved** | A later "changes requested" or a dismissal replaces it |
| It was given on the current head commit | Any new push. Approvals of an earlier commit are stale |
| The reviewer isn't the pull request's author | Authors can't approve their own change, on any channel |
| The reviewer isn't a bot | Accounts GitHub marks as `Bot`, such as `github-actions[bot]` |
| The reviewer isn't a coding agent | Logins that look like a coding agent's, such as ones containing `claude`, `copilot` or `cursor` |
| The reviewer is an allowed approver | The rule's `approvers`, its teams or CODEOWNERS, when the rule names them |

The agent check reads the login, so a person whose GitHub login looks like an agent's isn't counted either.

Other reasons an approval is missed:

- **The gate didn't re-run.** Re-run the **Bumpkit release gate** check, or the CI job that runs `bumpkit gate`.
- **Teams or CODEOWNERS can't be checked.** Without the Bumpkit GitHub App, the reason ends with "team membership can only be checked through the Bumpkit GitHub App" or reads "CODEOWNERS can only be checked through the Bumpkit GitHub App". Install the App, or list GitHub logins in `approvers`.
- **The token can't read reviews.** On GitLab CI, the default `CI_JOB_TOKEN` can't read merge request approvals. Set `GITLAB_TOKEN` or `BUMPKIT_GITLAB_TOKEN` with the `api` scope. See [Release Gate](/docs/release-gate#run-it-in-gitlab-ci).
- **A Slack or dashboard vote was refused.** Bumpkit tells the voter why, privately. See [Who can vote](/docs/approvals#who-can-vote).

**Fix.** Ask a different qualifying person to approve the latest commit. After every push, approve again.

## Bumpkit couldn't decide

**Symptom.** `bumpkit gate` prints an error instead of a decision and exits `2`, or the GitHub check fails with the title "Bumpkit couldn't check this pull request". Common causes:

```text
Bumpkit API error (HTTP 503): Auth service temporarily unavailable.
```

```text
Post "https://bumpkit.dev/v1/gate": dial tcp: lookup bumpkit.dev: no such host
```

**Why.** The gate fails closed: when Bumpkit can't finish a decision, the change doesn't pass. That covers an unreachable API, a server error, a GitHub API error while reading reviews or CI results, a policy Bumpkit couldn't read from the base branch (see [the base isn't a branch or tag](#the-gate-can-t-read-the-policy)).

[`on_error`](/docs/bumpkit-yml#on-error) decides what such an error means. With the default `fail`, the CLI exits `2` and the App's check concludes `failure`. With `on_error: pass`, the gate is advisory: the CLI prints the error, then passes and exits `0`, and the App's check concludes `neutral` (except with `require_check_apps` set, which always fails closed):

```text
Bumpkit couldn't decide, passing because on_error is pass: Bumpkit API error (HTTP 503): Auth service temporarily unavailable.
```

`on_error` never rescues an invalid `bumpkit.yml`; see [the policy is invalid](#the-policy-is-invalid).

### A check that couldn't run

Some checkers report that they couldn't finish, instead of guessing. The gate then waits for a person rather than passing silently:

| Finding kind | Title | Gate |
|---|---|---|
| `vulnerability_check_unavailable` | "Couldn't check 3 new or upgraded dependencies for known vulnerabilities" | Needs approval under `vulnerabilities` |
| `cross_repo_check_unavailable` (Beta) | "Couldn't read the service graph to find consuming repositories" or "Couldn't search for call sites in 2 of 12 consuming repositories" | Always needs approval under `cross_repo.require_approval` |

**Fix.** Re-run the job or the check; most of these are temporary. If the API is unreachable, check `BUMPKIT_API_URL` (or the Action's `api-url` input) and your network or proxy. If a checker stays unavailable, a reviewer can approve the change once they have looked. See [Cross-Repo Impact](/docs/cross-repo) for the Beta cross-repo check.

## The policy is invalid

**Symptom.** The CLI prints a `bumpkit.yml:` error and exits `2`. The GitHub check fails with the title "Invalid bumpkit.yml" and shows the error. `POST /v1/gate` answers `400` with code `INVALID_POLICY`. For example:

```text
bumpkit.yml: gate.on_error must be fail or pass
bumpkit.yml: gate.block_on: unknown kind "secrets" (use secret, data_loss, security_regression, breaking, contract_breaking, version_mismatch, cve_reachable, compatibility_break)
bumpkit.yml: gate.vulnerabilities must be high, medium or low
bumpkit.yml: gate.security_alerts must be critical, high, medium or low
bumpkit.yml: gate.hotfix.backports must be block, approve or off
bumpkit.yml: gate.hotfix.review_within must be a duration like 24h or 72h
bumpkit.yml: gate.require_checks has an empty name
bumpkit.yml: gate.deploy.require must be pass or approved
bumpkit.yml: line 4: count must be between 1 and 10
bumpkit.yml: line 3: when must be always, agent or agent_or_unknown
```

A misspelled or unknown key also fails, with the YAML parser's message naming the line.

**Why.** Bumpkit never falls back to a default when your policy has a mistake. If it did, a typo could turn the gate off. The gate fails closed, even with `on_error: pass`. If the App can't read the file at all, the check fails with the title "Couldn't read bumpkit.yml".

**Fix.** Correct the line the error names. The gate reads the policy from the base of the pull request, so a fix in the same pull request doesn't apply yet. Try the edited file first with:

```bash
bumpkit gate --policy bumpkit.yml
```

`--policy` checks the file locally. When the Bumpkit GitHub App is installed on the repository, the decision itself still uses the base branch's copy. Then merge the fix on its own. See [Invalid policy](/docs/bumpkit-yml#invalid-policy).

## A compatibility pair says not checked

**Symptom.** A pair in the [compatibility matrix](/docs/contract-checks#compatibility-matrix) shows `not_checked`.

**Fix.** Read the reason on the pair. Not checked means Bumpkit has no complete evidence, and it never means compatible. Turn on `contract_checks` and `version_consistency` if the reason names them. Then run a release check from one version tag to the next, inside the range. Checks recorded before `compatibility_matrix` was on don't count.

## The gate can't read the policy

**Symptom.** With the Bumpkit GitHub App installed on the repository, `bumpkit gate` answers `503` with code `POLICY_UNAVAILABLE`:

```text
Bumpkit couldn't read bumpkit.yml from the base branch of acme/api with the GitHub App, so the gate fails closed. Run the gate again.
```

**Why.** When the App is installed on the repository for your workspace, Bumpkit reads `bumpkit.yml` itself: from the base commit of a pull request into the default branch, and from the default branch otherwise. If GitHub can't return the file, the gate fails closed rather than trust a policy it couldn't check.

**Fix.** Run the gate again; it is usually a temporary GitHub error. See [The policy comes from the base](/docs/release-gate#the-policy-comes-from-the-base).

## The org policy can't be read

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `org_policies`.

**Symptom.** The GitHub check fails with the title "Couldn't read the org policy". The API answers `503` with code `ORG_POLICY_UNAVAILABLE` and one of these messages:

```text
Bumpkit couldn't read the org policy from acme/.github (the Bumpkit GitHub App isn't installed on it), so the gate fails closed.
Bumpkit couldn't read the org policy from acme/.github (it has no bumpkit.yml on its default branch), so the gate fails closed.
The org policy (version 7) doesn't parse, so the gate fails closed: org policy: locked must be a list of gate keys
```

**Why.** Your workspace has an org policy, stored in Bumpkit or in a config repository, and Bumpkit couldn't load it. Rather than gate with only the repository's rules, the decision fails. When the policy lives in a config repository, the reasons in brackets are: the GitHub App isn't set up, isn't installed on it, is installed for another workspace, GitHub didn't grant access, GitHub returned an error, or the repository has no `bumpkit.yml` on its default branch.

The CLI treats this `503` like any other API error, so a repository with `on_error: pass` passes in CI. Lock `on_error` in the org policy if that isn't what you want.

**Fix.** Install the Bumpkit GitHub App on the config repository from the same workspace, add or fix its `bumpkit.yml`, or save a corrected version of the stored policy. Then re-run the check. See [Org Policies & Freezes](/docs/org-policies#where-the-org-policy-lives).

## Rate limits

**Symptom.** The API answers `429`:

```json
{
  "status": "error",
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Minute rate limit reached. 120 requests allowed per minute.",
    "suggestion": "Wait for the rate-limit window to reset, then retry."
  }
}
```

The error also carries `explanation`, `next_step` and `docs_url`.

**Why.** Operational per-minute limits protect the service and are separate from billing, which is 1 credit = 1 release check = $0.10, with 60 free credits each month and no seat or repository limits. A workspace's limit is shared by all its keys. Responses carry `X-RateLimit-*` headers and `Retry-After` (seconds until the minute window resets). A few endpoints have their own limits and send their own `Retry-After`, for example `POST /v1/cli/login` ("Too many sign-in requests from this address. Try again in a minute.", `Retry-After: 60`) and `POST /hooks/errors/{token}` ("Too many error reports for this source. Batch events, up to 500 per request.", `Retry-After: 60`). The full list is in [Rate limits](/docs/api-auth#rate-limits).

**Fix.** Wait for the reset, or batch work into fewer calls.

## No tag to start from

**Symptom.**

```text
no tag to start from; pass --base
```

`bumpkit hotfix version` says `no version tag to start from; pass --from`, and `bumpkit deploy` warns `bumpkit deploy: no earlier tag, so no commits are sent; pass --base to attribute errors to commits`.

**Why.** Without a flag, Bumpkit starts a release from the latest tag reachable from `HEAD`. A new repository, or a checkout without tags, has none.

**Fix.** Pass the start yourself: `--base` for `check`, `gate` and `deploy`, and `--from` for `analyze`, `changelog`, `bump` and `hotfix version` (they print the same `pass --base` message, but their flag is `--from`). Or tag your first release, for example `git tag v0.1.0 && git push origin v0.1.0`. A [release train](/docs/release-trains) can't be cut until the repository has a release tag like `v1.0.0`.

### A multi-repo train won't start a step

**Cause.** The step says what holds it. `MULTI_TRAIN_ORDER_NEEDED` means Bumpkit can't prove the order: a repository isn't on the service map, the graph couldn't read it all, a cycle exists, or nothing connects the repositories. `MULTI_TRAIN_HELD` means an earlier step hasn't released.

**Fix.** Run `bumpkit train multi show TRAIN`. Have a person set the order on the train's page, or release the earlier step first. See [Release across repositories](/docs/release-trains#release-across-repositories).

## Shallow checkout

**Symptom.** In CI, the gate or check fails with:

```text
v1.4.2 is not a commit, tag or branch in .
```

or you see `no tag to start from; pass --base` although the repository has tags. Back-port detection may warn `Bumpkit couldn't check for hotfix fixes missing from this release:` followed by the git error.

**Why.** Many CI systems clone only the latest commit. Bumpkit needs the base commit, the tags and the history between them. `bumpkit gate` fetches a missing base itself; `bumpkit check` and the other commands don't.

**Fix.** Check out full history:

| CI | Setting |
|---|---|
| GitHub Actions | `fetch-depth: 0` on `actions/checkout` |
| GitLab CI | `GIT_DEPTH: "0"` |
| Bitbucket Pipelines | `clone: depth: full` |
| Azure Pipelines | `fetchDepth: 0` |
| Buildkite | Don't set `checkout.depth`, and add `--tags` to `BUILDKITE_GIT_FETCH_FLAGS` |
| CircleCI | `method: full` on `checkout` |
| Jenkins | Leave **Shallow clone** off, and keep **Fetch tags** on |

## Missing API key

**Symptom.**

```text
not signed in: run `bumpkit login`, or set BUMPKIT_API_KEY
```

The API answers `401` with `MISSING_API_KEY` ("No API key provided. Include your key in the x-api-key header."), `INVALID_API_KEY` or `REVOKED_API_KEY`.

**Why.** The CLI found no key in `BUMPKIT_API_KEY` or in its config file from `bumpkit login`, or the key was mistyped, revoked or expired. In GitHub Actions, secrets aren't passed to workflows triggered from forks.

**Fix.** On your machine, run `bumpkit login`. In CI, add the key as a secret named `BUMPKIT_API_KEY` and pass it to the step. Run `bumpkit whoami` to see which workspace and key the CLI uses. For the gate, a missing key counts as "couldn't decide": it exits `2`, or passes under `on_error: pass`.

## Feature not enabled

**Symptom.** A command or endpoint for a Beta feature answers `403` with code `FEATURE_EARLY_ACCESS`. The CLI prints only the message:

```text
Error: Agent scorecards is in early access. Request access at https://dashboard.bumpkit.dev/early-access?feature=agent_scorecards
```

A feature that is off answers `404` with code `NOT_FOUND`:

```text
Error: Bumpkit API error (HTTP 404): Route not found.
Next: Check the method, path and id against the API reference. A Beta feature your workspace doesn't have yet usually answers FEATURE_EARLY_ACCESS instead, with a link to request it. https://bumpkit.dev/docs/explanations#not-found
```

Some commands check first and say so directly, for example:

```text
Error: Intelligent rollback is not enabled for this workspace
```

**Why.** Beta features are in early access: they are on only for the workspaces they have been rolled out to so far. Its dashboard page or tab shows an early-access page instead, with a **Sign me up for early access** button. A feature that is off for everyone answers as if the route doesn't exist. MCP tools for a feature your workspace doesn't have don't appear at all.

**Fix.** Open the link in the message and sign up for early access; you get an email when it's on for your workspace. Run `bumpkit features` to list what is on. For the gate, a `403` or `404` counts as "couldn't decide".

## The GitHub check stays in progress

**Symptom.** **Bumpkit release gate** shows "Waiting for ci/test" and doesn't finish.

**Why.** The policy has [`require_checks`](/docs/bumpkit-yml#require-checks). When a required check has started on the commit but hasn't finished, the App waits, then decides as soon as it completes. A check that is stuck queued or running keeps the gate waiting.

**Fix.** Finish or re-run the stuck CI check, then re-run **Bumpkit release gate** if it doesn't update. Names in `require_checks` match check and status names without regard to case. A required check that never started on the commit isn't waited for: the gate fails it with "ci/test didn't run on this commit."

## The GitHub check failed after retries

**Symptom.** **Bumpkit release gate** fails with "Bumpkit couldn't finish this check".

**Why.** Bumpkit hit an error each time it tried to check the commit, for example while GitHub was unavailable, so the gate fails closed rather than letting the pull request through unchecked.

**Fix.** Push a commit or re-run **Bumpkit release gate** on GitHub to try again. The **Queued, running, retrying and failed pull request checks** panel on the Checks page, `bumpkit checks queue` and the `checks_queue` MCP tool show checks that are still retrying.

## GitHub Enterprise Server won't connect or report

- **"The host must be your GitHub Enterprise Server's https address on a public host."** Give the plain address, like `https://github.acme.example`, on port 443. IP addresses, other ports and names like `.internal` or `.local` are refused. For github.com, install the Bumpkit GitHub App.
- **No status appears on the pull request.** Check that your server can be reached from the public internet, and that the token has the `repo` scope. Bumpkit refuses to call a host that resolves to a private address.
- **The webhook delivery shows `400`.** Set **Content type** to `application/json` on the webhook.
- **The webhook delivery shows `401`.** The secret on the webhook doesn't match. Remove the connection and add it again to get a new one.
