Skip to content
Latchkey

Composer "Failed to download ... 403" in CI

Composer resolved the package but the download of its dist archive returned 403. On github.com this is most often the unauthenticated API rate limit; on other hosts it means the credentials lack access to that artifact.

What this error means

composer install prints "Failed to download vendor/package from dist: ... 403 Forbidden" and may fall back to source before failing.

Composer
  - Downloading vendor/package (1.2.3)
Failed to download vendor/package from dist:
  The "https://api.github.com/repos/vendor/package/zipball/..." file could not
  be downloaded (HTTP/1.1 403 Forbidden)

Common causes

GitHub API rate limit for unauthenticated requests

Downloading dist zipballs from github.com without a token exhausts the anonymous limit and returns 403.

Credentials lack access to the artifact host

On a private or mirrored host, the configured token is not permitted to download that archive.

How to fix it

Authenticate the download

  1. Provide a github-oauth token via COMPOSER_AUTH to lift the API rate limit.
  2. For other hosts, add the matching http-basic credentials.
  3. Re-run so dist downloads are authenticated.
.github/workflows/ci.yml
env:
  COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GITHUB_TOKEN }}"}}'

Cache dist archives to reduce downloads

Persist the Composer cache so already-downloaded archives are not re-fetched each run.

.github/workflows/ci.yml
- uses: actions/cache@v4
  with:
    path: ~/.cache/composer
    key: composer-${{ hashFiles('composer.lock') }}

How to prevent it

  • Always authenticate dist downloads with COMPOSER_AUTH in CI.
  • Cache the Composer directory on the lock hash.
  • Use least-privilege tokens for private artifact hosts.

Related guides

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