GitHub Actions codecov/codecov-action "Token required" / Upload Rejected
codecov/codecov-action could not upload coverage. Private repos require a token, the coverage file may be missing, or the upload endpoint had a transient error.
What this error means
The codecov step fails saying a token is required, that no coverage reports were found, or with a transient upload error. Coverage does not appear on Codecov for the run.
Error: Codecov token not found. Please provide a token via the token input.
# or
['error'] There was an error fetching the storage URL during upload: 503Diagnose it: what token do you actually have?
Permission failures in Actions are almost never about your repository settings alone. Three things combine: the default GITHUB_TOKEN permission set for the repo or organization, the permissions: block in the workflow, and whether the event is a fork pull request, which downgrades the token to read-only regardless of everything else.
- name: Show the token scopes actually granted
run: |
curl -sI -H "Authorization: Bearer $GITHUB_TOKEN" \
https://api.github.com/ | grep -i "^x-oauth-scopes\|^x-accepted"
echo "event: ${{ github.event_name }}"
echo "fork PR: ${{ github.event.pull_request.head.repo.fork }}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}Common causes
Missing token on a private repo
Tokenless upload works for some public repos, but private repos require CODECOV_TOKEN passed to the action, or the upload is rejected.
No coverage file or transient endpoint error
If the test step did not produce a coverage report at the expected path, there is nothing to upload; separately, the upload endpoint can fail transiently with a 5xx.
How to fix it
Pass the token and point at the report
- uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.infoConfirm the report exists; retry blips
- Ensure the test step actually wrote a coverage file at the path you reference.
- Set fail_ci_if_error thoughtfully so a transient 5xx does not block merges.
- Re-run when the failure is an upload-endpoint 5xx rather than a missing token.
Grant the narrowest permission that works
Declaring a permissions: block switches the job from the repository default to exactly what you list, so an incomplete block is a common cause of a new failure right after someone tightened security. List every scope the job needs, not just the one that failed.
permissions:
contents: read # checkout
packages: write # push to GHCR
id-token: write # OIDC to a cloud provider
pull-requests: write # comment on or label a PR
checks: write # publish check runsHow to prevent it
- Provide CODECOV_TOKEN for private repositories.
- Verify the coverage report path before uploading.
- Treat upload-endpoint 5xx errors as retryable.