Skip to content
Latchkey

Composer "Could not authenticate against github.com" (401) in CI

Composer fetches package metadata and zip archives from GitHub. Unauthenticated requests are rate-limited and private repos require a token. When Composer has no valid GitHub token, it returns HTTP 401 and prompts for credentials it cannot read in non-interactive CI.

What this error means

composer install fails with "Could not authenticate against github.com" or "The 'https://api.github.com/...' URL returned: 401". It commonly appears mid-build once the unauthenticated rate limit is hit.

composer
Could not authenticate against github.com

  Failed to download acme/widget from dist: The
  "https://api.github.com/repos/acme/widget/zipball/abc123" file could
  not be downloaded (HTTP/2 401 )

Common causes

No GitHub token in non-interactive CI

Composer cannot prompt for credentials in CI, so private repos and rate-limited API calls fail with 401.

Unauthenticated GitHub API rate limit exhausted

Anonymous requests share a low hourly limit; busy CI bursts past it and GitHub starts returning 401/403 until it resets.

How to fix it

Provide a GitHub token to Composer

Configure an OAuth token from a secret so authenticated requests have a much higher rate limit and reach private repos.

Terminal
composer config --global --auth github-oauth.github.com \
  "${{ secrets.COMPOSER_GITHUB_TOKEN }}"
composer install --no-interaction

Use the auth.json env variable

Pass credentials via COMPOSER_AUTH so nothing is committed.

.github/workflows/ci.yml
env:
  COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.COMPOSER_GITHUB_TOKEN }}"}}'

How to prevent it

  • Always provide a GitHub token to Composer in CI via COMPOSER_AUTH or github-oauth config.
  • Use a least-privilege token and store it as a secret, never in the repo.
  • Cache Composer downloads so fewer authenticated requests are needed per build.

Related guides

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