Skip to content
Latchkey

ARC authentication "401 Bad credentials" in CI

GitHub rejected the credential with HTTP 401 before any permission check. For ARC this almost always means the App private key or IDs in githubConfigSecret are wrong, or the PAT is expired or revoked.

What this error means

The controller or listener logs "401 Bad credentials" or "could not refresh token". Nothing registers because authentication fails at the transport level.

controller
ERROR could not create registration token
  {"error": "POST https://api.github.com/app/installations/12345/access_tokens:
  401 Bad credentials []"}

Common causes

Malformed or wrong App private key

The PEM in the secret was truncated, double base64 encoded, or belongs to a different App, so token exchange returns 401.

Expired or revoked PAT

If you authenticate with a PAT rather than an App, an expired or revoked token yields 401 Bad credentials on every call.

How to fix it

Recreate githubConfigSecret from the raw values

  1. Download a fresh private key PEM from the App settings.
  2. Recreate the secret with the App ID, installation ID, and the PEM file directly.
  3. Restart the controller so it reloads the secret.
Terminal
kubectl create secret generic pre-defined-secret \
  --namespace arc-runners \
  --from-literal=github_app_id=123456 \
  --from-literal=github_app_installation_id=7891011 \
  --from-file=github_app_private_key=./app.private-key.pem

Rotate an expired PAT

If using github_token, generate a new PAT with the runner admin scope and update the secret, then restart the controller.

How to prevent it

  • Store the private key as a file, never hand-edited, to avoid truncation.
  • Prefer a GitHub App so credentials do not expire like PATs.
  • Alert on 401 in controller logs so a rotated key is caught early.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →