GitLab CI artifact download fails - expired or 403 forbidden
Downloading artifacts returns 403 Forbidden or 404 Not Found
Focused incident mode — strips away everything but this runbook.
Symptoms
- Downloading artifacts returns 403 Forbidden or 404 Not Found
- GitLab UI shows 'No artifacts' for a pipeline that previously had them
- downstream job fails with 'Could not download artifact'
GitLab automatically deletes artifacts after expire_in. If a downstream pipeline depends on artifacts from an upstream pipeline, both must run within the expiration window.
Evidence to grab
Collect these artifacts before you change anything — they prove (or rule out) each cause.
Artifact expiration setting
grep -A10 'artifacts:' .gitlab-ci.yml | grep expire_inPipeline artifact status
glab api /projects/<PROJECT_ID>/pipelines/<PIPELINE_ID>/artifacts
Diagnostic flow
Run the steps in order, marking each claim confirmed or refuted. The evidence you gather feeds the ranked causes below.
Guided decision tree is ready.
Steps with commands, expected output, and pass/fail branches.
Ranked root causes
Evidence checklist
- expire_in: 1 hour but the downstream job runs 2 hours later
- Pipeline artifacts endpoint returns 404
Confirm before fixing
glab api /projects/<PROJECT_ID>/jobs/<JOB_ID>/artifacts 2>&1 | head -5Check if artifacts exist
Fix
sed -i 's/expire_in: 1 hour/expire_in: 30 days/' .gitlab-ci.ymlIncrease the artifact expiration period
Resolution runbook
Apply from top to bottom after the cause is confirmed. Review every command before running it — risk tags are shown on each.
glab api /projects/<PROJECT_ID>/jobs/<JOB_ID>/artifacts 2>&1 | head -5Check artifact existence
grep -B5 -A15 'artifacts:' .gitlab-ci.ymlReview expiration settings
sed -i 's/expire_in: 1 hour/expire_in: 30 days/' .gitlab-ci.ymlExtend the expiration if too short
Prevention
- Set expire_in on artifacts to at least the maximum time between dependent pipeline runs.
- Use 'when: on_success' and dependencies: to ensure artifacts are fetched promptly.
- For long-lived artifacts, use GitLab's 'dependencies' keyword or download them to external storage.
- Monitor artifact storage usage in the project usage quotas.
Related runbooks
Same stack, same failure family — worth a skim.
Pipeline shows 'Blocked' status and cannot proceed
Secret appears in plaintext in the job log instead of being masked
Commands marked READ CHANGE DESTRUCTIVE — never run DESTRUCTIVE commands against production data you cannot restore. Copy → verify → execute.