mediumFoundationSymptom-first#artifacts#expire#403#download

GitLab CI artifact download fails - expired or 403 forbidden

Downloading artifacts returns 403 Forbidden or 404 Not Found

Likely source:data-lossconfiguration

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_in
  • Pipeline 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 -5
READ

Check if artifacts exist

Fix

sed -i 's/expire_in: 1 hour/expire_in: 30 days/' .gitlab-ci.yml
CHANGE

Increase 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.

1
glab api /projects/<PROJECT_ID>/jobs/<JOB_ID>/artifacts 2>&1 | head -5
READ

Check artifact existence

2
grep -B5 -A15 'artifacts:' .gitlab-ci.yml
READ

Review expiration settings

3
sed -i 's/expire_in: 1 hour/expire_in: 30 days/' .gitlab-ci.yml
CHANGE

Extend 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.

Commands marked READ CHANGE DESTRUCTIVE — never run DESTRUCTIVE commands against production data you cannot restore. Copy → verify → execute.