Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for GitLab CI/CD By James Joyner IV · · 8 min read

GitLab CI Error Guide: 'chosen stage does not exist' — Fix Stage Ordering

Quick answer

Fix 'chosen stage does not exist' in GitLab CI: declare every job stage in the top-level stages list, match names exactly, and order stages so jobs resolve cleanly.

  • #gitlab
  • #ci-cd
  • #troubleshooting
  • #errors
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Overview

GitLab’s pipeline parser rejects the entire .gitlab-ci.yml before a single job runs when a job references a stage: that is not declared in the top-level stages: list. The pipeline never starts; the MR shows a config error instead of a running pipeline:

Found errors in your .gitlab-ci.yml:
  jobs:deploy job: chosen stage does not exist; available stages are .pre, build, test, .post

Because this is a validation error, no jobs execute at all — one mistyped stage name blocks the whole pipeline, not just the offending job.

Symptoms

  • The pipeline is marked failed immediately with a YAML/config error, no jobs created.
  • The message lists the “available stages” — the exact set from your stages: block plus the implicit .pre and .post.
  • A job you recently added or renamed is the one referenced in the error.
  • CI Lint flags the job as invalid while the rest of the file looks fine.
  • After an include: refactor, a job from an included file references a stage the parent never declared.

Common Root Causes

  • Stage not listed — the job’s stage: value is spelled correctly but was never added to the top-level stages: list.
  • Name mismatch / typostage: deploy vs. stages: [..., deployment], or a trailing space / different case.
  • Included file assumes a stage — an included template uses a stage name the including project didn’t declare.
  • Relying on defaults after customizing — once you define a custom stages: list, the built-in build/test/deploy defaults no longer exist unless you re-list them.
  • .pre/.post confusion — trying to use a custom stage where only the reserved .pre/.post are appropriate, or misspelling them (pre, .Pre).
  • YAML anchor/extends drift — a job inherits stage: from a template that points at a stage removed during a refactor.

Diagnostic Workflow

First validate the config with the CI Lint API, which reports the same parser error locally:

# Validate .gitlab-ci.yml via CI Lint API (replace HOST, TOKEN, PROJECT_ID)
lint-check:
  image: curlimages/curl:latest
  script:
    - |
      curl --silent --header "PRIVATE-TOKEN: $LINT_TOKEN" \
        "https://$CI_SERVER_HOST/api/v4/projects/$CI_PROJECT_ID/ci/lint" \
        --data-urlencode "content=$(cat .gitlab-ci.yml)"

Or run the fully-merged validation (expands include: and extends:) with glab:

# Locally, using the glab CLI — resolves includes before checking stages
# glab ci lint .gitlab-ci.yml

Then list every stage each job references and compare to the declared list:

stages:
  - build
  - test
  - deploy        # every job's `stage:` MUST appear here

deploy-prod:
  stage: deploy   # matches the list exactly — no typo, no case drift
  script:
    - ./deploy.sh

If the offending job comes from an include, view the merged YAML in the pipeline editor’s “Full configuration” tab to see the effective stages: and every stage: reference in one place.

Example Root Cause Analysis

A team split their pipeline into templates with include:. The main file declared:

stages: [build, test]

include:
  - local: ci/deploy.yml

The included ci/deploy.yml contained:

deploy-staging:
  stage: deploy      # <-- 'deploy' was never added to the parent stages list
  script: ./deploy.sh staging

The pipeline failed instantly with chosen stage does not exist; available stages are .pre, build, test, .post. The included template assumed a deploy stage that the parent project never declared. Because included files share the parent’s single stages: list, the fix was to add deploy to the top-level list:

stages: [build, test, deploy]

After adding the stage, the pipeline parsed and the deploy job scheduled in the correct order. The team also added a CI Lint step to their pre-merge checks so a future template referencing an undeclared stage is caught before merge.

Prevention Best Practices

  • Declare a complete top-level stages: list up front and treat it as the single source of truth every job must match.
  • Run CI Lint (API or glab ci lint) in a pre-merge job so undeclared stages fail at review, not on the default branch.
  • When using include:, keep the stages: list in the parent and document which stages templates may use.
  • Avoid re-typing stage names in each job — reference a small, known set and use extends: templates that point at declared stages.
  • Use the pipeline editor’s “Full configuration” view after any include/extends refactor to confirm every stage: resolves.
  • Prefer lowercase, hyphen-free stage names consistently to eliminate case/typo mismatches.

Quick Command Reference

# Validate the merged config locally
glab ci lint .gitlab-ci.yml

# Validate via CI Lint API (with include expansion)
curl --header "PRIVATE-TOKEN: $TOKEN" \
  "https://gitlab.example.com/api/v4/projects/$PROJECT_ID/ci/lint" \
  --data-urlencode "content=$(cat .gitlab-ci.yml)" \
  --data "include_merged_yaml=true"

# Grep every stage reference and compare to the declared list
grep -nE '^\s*stage:' .gitlab-ci.yml ci/*.yml

Conclusion

chosen stage does not exist is a pure configuration error: a job points at a stage the top-level stages: list never declared, usually from a typo, a case mismatch, or an included template assuming a stage the parent didn’t add. Because it blocks the whole pipeline, fix it by making the declared stages: list complete and exact, then guard it with a CI Lint step so the next undeclared stage is caught before merge instead of on your default branch.

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.