GitLab CI Error Guide: 'downstream pipeline can not be created' — Fix Broken Trigger and Child Pipelines
Fix 'downstream pipeline can not be created' in GitLab CI: repair invalid child YAML, missing trigger files, empty jobs, ref/branch mismatches, and CI/CD variable errors so bridge jobs generate pipelines.
- #gitlab
- #ci-cd
- #troubleshooting
- #errors
Stuck on this GitLab CI/CD error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
A trigger: job (a bridge) in GitLab does not run a script — it hands GitLab a configuration and asks it to create a downstream pipeline (a child pipeline in the same project, or a multi-project pipeline in another project). When GitLab accepts the config but cannot turn it into a real pipeline, the bridge job fails and the pipeline graph shows the downstream node in red with this message:
downstream pipeline can not be created
You will also see closely related variants surfaced on the bridge job or in the pipeline’s “Failed Jobs” tab:
The parent pipeline job could not create the child pipeline
config should contain at least one visible job
downstream pipeline can not be created, Ref is ambiguous
Unlike an invalid CI config error on the parent, this failure happens at the bridge — the parent pipeline started fine, but the generated or referenced child config is empty, invalid, or points at something that does not exist.
Symptoms
- The bridge/trigger job shows a red “failed” status with no job log (bridges have no trace) or a one-line error.
- The child pipeline node in the pipeline graph is missing or marked failed with
downstream pipeline can not be created. - Dynamic child pipelines that worked yesterday fail after a refactor of the generator job.
- Multi-project triggers fail while the same YAML works when run directly in the downstream project.
- The generated artifact (e.g.
generated-config.yml) is empty, zero-byte, or contains only comments/hidden jobs.
Common Root Causes
- Empty or all-hidden child config — the generated/referenced YAML contains only hidden jobs (keys starting with
.), templates, or comments. A pipeline needs at least one visible job. - Missing generated artifact — a dynamic child pipeline references a file the upstream generator job did not actually produce (wrong path, job failed silently, artifact expired or not passed via
needs/artifacts). - Invalid child YAML — the generated config has a syntax error or references an undefined
extends/includethat only fails when GitLab compiles the child. - Bad
include:in the child —trigger: include:points at a file that does not exist on the target ref. - Ref / branch mismatch — a multi-project
trigger:targets abranch:or ref that does not exist in the downstream project. rulesproduce no jobs — every job in the child config is filtered out byrules:/workflow:, leaving zero jobs to run.- Variable/forwarding errors — a variable expected by the child (used in
rules:or aninclude) is never forwarded, so the child compiles to nothing. - Nesting/limit exceeded — child pipelines nested too deep, or downstream pipeline limits hit.
Diagnostic Workflow
1. Read the exact error on the bridge job. In the pipeline view, click the failed downstream node or open the “Jobs” tab and filter to the bridge. The suffix after the message (Ref is ambiguous, config should contain at least one visible job) tells you which cause below applies.
2. For dynamic child pipelines, verify the generator actually produced the file. The classic pattern — confirm the artifact path matches the trigger include path exactly:
generate-config:
stage: build
script:
- ./scripts/generate-pipeline.sh > generated-config.yml
# fail loudly if the generator produced nothing
- test -s generated-config.yml || { echo "generated config is empty"; exit 1; }
artifacts:
paths:
- generated-config.yml
run-child:
stage: test
needs:
- job: generate-config
artifacts: true
trigger:
include:
- artifact: generated-config.yml
job: generate-config
strategy: depend
If the generator emits an empty file, add the test -s guard shown above so the generator fails clearly instead of the opaque bridge error downstream.
3. Validate the generated/child YAML with CI Lint. Copy the generated content and validate it. Use the CI Lint page (CI/CD → Editor → Validate, or the project’s /-/ci/lint) or the API:
# Validate a child config against the project, with variables expanded
curl --silent --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.example.com/api/v4/projects/$CI_PROJECT_ID/ci/lint" \
--data-urlencode "content@generated-config.yml" \
--data "include_jobs=true" | jq '{valid, errors, merged_yaml}'
If valid is true but the pipeline still fails to create, look at merged_yaml — if there are no visible jobs after merge, that is your “at least one visible job” failure.
4. Confirm at least one visible job survives rules. A job whose every rule evaluates to when: never is dropped. Temporarily add an always-on job to prove the child can be created:
# Minimal known-good child config for isolation
smoke:
stage: test
script:
- echo "child pipeline created successfully"
rules:
- when: always
If the child now creates, the problem is your rules:/workflow: filtering everything out — not the trigger wiring.
5. For multi-project triggers, check the target ref exists. A branch: that does not exist downstream yields Ref is ambiguous or a create failure:
deploy-downstream:
stage: deploy
trigger:
project: my-group/deploy-project
branch: main # must exist in deploy-project
strategy: depend
Verify the branch/tag actually exists in the downstream project and that the trigger has permission (the triggering user/token must be able to run pipelines there).
6. Check variable forwarding. If the child’s rules: or include: depend on a variable, forward it explicitly:
run-child:
variables:
DEPLOY_ENV: staging
trigger:
include: child/.gitlab-ci.yml
forward:
pipeline_variables: true
Example Root Cause Analysis
A monorepo used a dynamic child pipeline: a generate job wrote pipeline.yml containing one job per changed service. After a refactor, the bridge started failing with config should contain at least one visible job.
Diagnosis:
- The bridge job had no log, so the team ran CI Lint on the artifact — it reported
valid: truebutmerged_yamlcontained only a single hidden.service-template:job. - Reading the generator script showed it emitted the shared
.service-templateanchor but, when no service directories changed, produced zero concrete jobs — leaving only the hidden template. - On the MR that touched only
README.md, the change-detection loop matched nothing, so the child had no visible jobs and GitLab refused to create it.
Fix: the generator now emits a fallback no-op job when nothing matches, and a test -s guard plus a visible-job assertion:
noop:
stage: test
script:
- echo "No service changes detected; nothing to build."
rules:
- when: always
The bridge stopped failing because every generated config now contains at least one visible job. The team also added the CI Lint API call to the generator job so an invalid child config fails at generation time with a readable error.
Prevention Best Practices
- Always guard generated configs with
test -s generated-config.ymlso an empty file fails the generator, not the bridge. - Emit a fallback no-op job in dynamic pipelines so the child never compiles to zero visible jobs.
- Validate generated child YAML with the CI Lint API inside the generator job before triggering.
- Match the
artifact:path intrigger: include:exactly to the generator’sartifacts: paths:entry, and wire them withneeds: [{ job:, artifacts: true }]. - For multi-project triggers, pin and verify the downstream
branch:/ref, and confirm trigger permissions. - Forward the variables the child needs with
forward: pipeline_variables: true(or explicitvariables:), and keep childrules:from filtering out every job. - Use
strategy: dependso downstream failures propagate visibly instead of the parent going green while the child is broken.
Quick Command Reference
# Validate a child/generated config (variables + jobs expanded)
curl -s --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/ci/lint" \
--data-urlencode "content@generated-config.yml" \
--data "include_jobs=true" | jq '{valid, errors}'
# Assert a generated config is non-empty (put in the generator job)
test -s generated-config.yml || { echo "empty child config"; exit 1; }
# List recent pipelines to find the failed bridge/child relationship
curl -s --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/pipelines?order_by=id&sort=desc" | jq '.[0:5]'
# Check a downstream project's branches exist before triggering
curl -s --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$CI_API_V4_URL/projects/<downstream-id>/repository/branches" | jq '.[].name'
Conclusion
downstream pipeline can not be created almost always means the child config GitLab was handed is empty, invalid, or points at something that does not exist — not that the trigger keyword is wrong. Read the exact suffix on the bridge job, validate the generated or referenced YAML with CI Lint, and confirm at least one visible job survives your rules:. Guarding generated configs against emptiness and adding a fallback no-op job eliminates the most common cause for good, so dynamic and multi-project pipelines create reliably every time.
Fixed it? Get 500 GitLab CI/CD & 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.
Did this fix your issue?
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.