GitLab CI: 'jobs config should contain...' — Fixing Invalid .gitlab-ci.yml

GitLab refuses to run the pipeline and shows a red 'yaml invalid' banner. It's almost always indentation or a missing script — and GitLab ships a linter that points at the exact line.

BytExplorer 6 min read July 18, 2026

You push and instead of a pipeline you get a red banner:

Found errors in your .gitlab-ci.yml:
  jobs config should contain at least one visible job

or

(<unknown>): did not find expected key while parsing a block mapping

GitLab validates .gitlab-ci.yml before running anything. These errors are configuration problems, not build failures — nothing ran. YAML is whitespace-sensitive, so most of them are indentation. GitLab gives you a linter that removes the guesswork.

First: use the CI Lint tool

Go to Build → Pipeline editor (it validates live) or CI/CD → Editor → Lint. Paste your file and it reports the exact line and reason. This is faster than pushing repeatedly.

Cause: indentation (spaces, never tabs)

YAML forbids tabs for indentation. One stray tab, or a key indented by three spaces where its siblings use two, breaks the whole parse:

build:
  script:
    - make build      # 4 spaces, consistent
	- make test       # ❌ this line used a TAB → parse error

Make your editor show whitespace and use 2 spaces everywhere. This is the cause behind most "did not find expected key" messages.

Cause: a job with no script

Every normal job needs a script (the thing to run):

test:
  stage: test        # ❌ no script → invalid job

Add one:

test:
  stage: test
  script:
    - pytest

"jobs config should contain at least one visible job" often means every job is hidden (see below) or malformed, so GitLab sees zero runnable jobs.

Cause: stage not declared in stages

If a job names a stage that isn't in the top-level stages list, it's rejected:

stages: [build, test]

deploy:
  stage: deploy      # ❌ 'deploy' isn't in stages
  script: [./deploy.sh]

Add deploy to stages, or use an existing stage.

Cause: everything is a hidden job

A key starting with a dot is a template, not a runnable job:

.build:              # hidden — a reusable template, never runs on its own
  script: [make]

If all your jobs start with ., GitLab sees "no visible job". Remove the dot from the ones that should run, or extends: them from a real job.

Cause: a typo'd keyword

scripts: (should be script:), on_success misspelled, only/rules mis-nested — GitLab only allows a fixed set of keys. The Lint tool flags unknown keys directly.

The checklist

  1. Run the file through CI Lint / Pipeline editor — it names the line.
  2. 2 spaces, no tabs, consistent indentation everywhere.
  3. Every runnable job has a script:.
  4. Every job's stage: appears in the top-level stages: list.
  5. Jobs starting with . are hidden templates — remove the dot to run them.
  6. Check for typo'd keywords (script not scripts).

Frequently Asked Questions

What does 'jobs config should contain at least one visible job' mean?

GitLab found no runnable jobs. Usually every job is either hidden (its name starts with ., making it a template) or malformed (missing script). Make at least one job visible and valid.

Can I use tabs in .gitlab-ci.yml?

No. YAML prohibits tabs for indentation — they cause parse errors like "did not find expected key". Use spaces only (2 is conventional) and enable "show whitespace" in your editor to catch stray tabs.

How do I validate my GitLab CI file before pushing?

Use the built-in CI Lint (CI/CD → Editor → Lint) or the Pipeline editor, which validates as you type and points at the exact offending line — much faster than pushing commits to test.

Put it into practice

Stop reading, start building

This pairs with a hands-on BytExplorer course — do it on your own machine and actually keep the skill.

More in Troubleshooting