Troubleshooting

Error: no GitHub token found; pass --token, set GITHUB_TOKEN/GH_TOKEN, or run gh auth login

No token was found in --token, GITHUB_TOKEN, or GH_TOKEN — and either the gh CLI isn’t installed, or gh auth token failed (not logged in, or logged into the wrong host). Set one of the first three explicitly, or run gh auth login and try again. See SECURITY.md for the required token permissions and what the gh auth token fallback trusts.

GitHub API error 403 ... Resource not accessible by integration (inside a GitHub Action)

You’re using the automatically-generated secrets.GITHUB_TOKEN. This will never work, regardless of the permissions: block on your workflow — GITHUB_TOKEN has no permission scope covering repository administration. Create a PAT (classic repo scope, or fine-grained Administration: Read and write), store it as a repository secret, and reference that secret instead. See the README’s GitHub Action example.

GitHub API error 401 ... Bad credentials

The token is present but invalid or expired. Regenerate it.

invalid repository 'xyz'; expected 'owner/name'

--repo (or a resolved GITHUB_REPOSITORY) didn’t contain a /. Pass the full owner/name form.

could not determine repository; pass --repo owner/name

None of --repo, $GITHUB_REPOSITORY, or a git remote get-url origin in the current directory resolved to a repository. This also fires if git isn’t installed and no --repo/ $GITHUB_REPOSITORY was supplied.

Recognised origin shapes: git@github.com:owner/name, https://github.com/owner/name, ssh://git@github.com/owner/name, and SSH host aliases like git@github.com-work:owner/name, each with or without .git. Anything else (a GitHub Enterprise host, a non-GitHub remote) needs --repo.

policy file not found: <path> / policy file is empty: <path>

Check --config’s path is correct relative to your current working directory (CLI) or the Action’s config: input is relative to the repo root (Action).

invalid policy config in <path>: ...

validate’s or audit’s Pydantic error output — each line names the exact field and what’s wrong with it (e.g. branches.main.enforcement: unsupported value).

apply reports a change every single run, even though nothing should be different

This was the strict-mode phantom-drift bug (fixed — see docs/test-strategy.md) on versions before it shipped. Upgrade. If it recurs on a current version, it’s a new bug: file an issue with the exact plan output and your policy.yml.

A ruleset got deleted that I didn’t expect

strict: true prunes any repo-policy:*-named ruleset whose branch is no longer declared in policy.yml (see ../ARCHITECTURE.md’s Apply Safety Model). Check whether the branch was recently removed from the config, and whether strict is set at the top level (it applies as a default to every branch unless overridden). This never touches a ruleset that isn’t named repo-policy:*.

apply completed but policy is not converged (exit code 1)

Every mutation apply attempted succeeded (or none were needed), but the independent, fresh re-check it runs immediately afterward (cli.verify_after_apply) still finds drift, or a declared repo_settings field still comes back unavailable. This is not a partial failure — nothing raised — it means GitHub’s own state doesn’t match what the mutation calls’ 2xx responses implied it would. Common causes: a declared field GitHub silently ignores or resets under a specific combination (see docs/test-strategy.md’s “What mocking alone could not catch” for real examples of this), eventual-consistency lag on GitHub’s effective-rules view for a just-updated ruleset (a second apply often resolves this on its own), or an org-level policy overriding what repo-policy just set. Re-run repo-policy plan against the same config/repo to see exactly which field(s) are still reported as drift, and apply again — if it doesn’t converge after two attempts, treat it as a real bug and file an issue with both plan outputs attached.

repo settings: delete_branch_on_merge unavailable on this repository (exit code 1)

The token can’t see the field. GET /repos/{owner}/{repo} omits delete_branch_on_merge and allow_update_branch entirely for some token scopes (a fine-grained PAT with Administration: Read-only is one confirmed case), and repo-policy refuses to guess. Either drop the field from repo_settings, or use a token that returns it. Confirm what your token sees with:

curl -s -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/repos/<owner>/<repo> \
  | python3 -c "import sys, json; d = json.load(sys.stdin); print({k: d.get(k, '<ABSENT>') for k in ('delete_branch_on_merge', 'allow_update_branch')})"

apply exits 3 partway through, but the output shows some branches already applied

This is PartialApplyError: one branch (or repo-setting) mutation failed partway through the same apply run’s mutation phase — zero or more earlier resources in that phase may already have been mutated successfully before it. The printed journal lines (<resource>: applied N change(s) / <resource>: no changes needed / <resource>: failed -- N change(s) not applied) are the ground truth for what happened before the failure — nothing before the failed line was rolled back, since repo-policy has no transaction concept across resources (each branch/repo-setting mutation is its own independent API call). Read the accompanying error message for the underlying GitHubAPIError (rate limiting, a transient 5xx, a permissions gap partway through a token’s scope) and re-run apply once it’s addressed — an already-applied resource simply reports zero further changes needed on the next run (see ARCHITECTURE.md’s “Apply Outcomes and Exit Codes”).

could not initialize GitHub client: ... (exit code 2)

repo-policy sits behind an httpx.Client, which honors the standard proxy environment variables: HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, and NO_PROXY (comma-separated hostnames to bypass the proxy for). HTTP_PROXY/HTTPS_PROXY/ALL_PROXY may use http://, https://, socks5://, or socks5h:// schemes — SOCKS support ships by default (via the httpx[socks] extra), so no separate install step is needed.

If GitHub client construction itself fails (not a request — the client never got far enough to make one), this is always a setup problem, not policy drift: it exits 2 (EXIT_CONFIG_ERROR), the same code as a missing token or malformed --repo, never 1 (EXIT_DRIFT) and never an uncaught traceback. Check the accompanying message — it names what’s likely wrong (e.g. an unreachable or malformed proxy URL). If you’re running against a mirrored or vendored install that dropped the httpx[socks] extra, reinstall repo-policy to restore it.

Docker image fails to build with Readme file does not exist: README.md

If you’re building the Dockerfile yourself: it must be built with the repo root as build context, and README.md must be copied in alongside pyproject.toml — hatchling’s build validates the readme field declared in pyproject.toml at install time.