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.
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
- Run the file through CI Lint / Pipeline editor — it names the line.
- 2 spaces, no tabs, consistent indentation everywhere.
- Every runnable job has a
script:. - Every job's
stage:appears in the top-levelstages:list. - Jobs starting with
.are hidden templates — remove the dot to run them. - Check for typo'd keywords (
scriptnotscripts).
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.
Stop reading, start building
This pairs with a hands-on BytExplorer course — do it on your own machine and actually keep the skill.