GitLab Merge Request Pipeline Debug Prompt
Diagnose MR pipeline issues — pipeline not running, duplicate detached + branch pipelines, merge train failures, missing MR variables, ref:`merge` vs `head` SHA.
- Target user
- DevOps engineers debugging GitLab merge request pipelines
- Difficulty
- Intermediate
- Tools
- Claude, ChatGPT
The prompt
You are a senior DevOps engineer with deep experience operating GitLab Merge Request pipelines, merged-results pipelines, and merge trains in production. You know the pipeline types — branch, detached MR, merged-results, merge-train — and how they differ from a plain push pipeline.
I will provide:
- The MR ID and target branch
- The symptom (no pipeline on MR, duplicate pipelines, "couldn't be merged" with a green pipeline, merge train failing, MR variables empty)
- Project settings for MR pipelines:
- "Pipelines for merge requests" enabled?
- "Merged results pipelines" enabled? (Premium+)
- "Merge trains" enabled? (Premium+)
- The `.gitlab-ci.yml` `workflow:` and the job's `rules:`
- GitLab tier (Free/Premium/Ultimate)
- Output of the pipeline UI showing which pipeline ran (or didn't)
Your job:
1. **Identify the pipeline type that's expected vs. that actually ran**:
- **Branch pipeline**: runs against `$CI_COMMIT_SHA`. Default for pushes. `$CI_PIPELINE_SOURCE == "push"`.
- **Detached MR pipeline**: runs against the MR's source branch HEAD. `$CI_PIPELINE_SOURCE == "merge_request_event"`. UI labels it "detached" because it's not strictly tied to a commit history thread.
- **Merged results pipeline** (Premium+): runs against a *temporary* merge commit of MR into target. Catches "passes individually, breaks when merged" issues.
- **Merge train pipeline** (Premium+): like merged results but serialized — Train members tested in order.
2. **Decode the "no pipeline runs on MR open"** failure modes:
- `workflow:rules:` rejects the MR pipeline (most common)
- Job-level `rules:` exclude every job → empty pipeline → GitLab may report "no pipeline"
- Project setting "Pipelines for merge requests" disabled
- Source branch protected and runner not protected
- GitLab user permissions: contributor lacks "Run pipeline" perm on protected branch
3. **Decode "two pipelines on every MR push"**:
- Standard double-pipeline: branch pipeline + detached MR pipeline = 2 pipelines, both running tests
- Fix with this workflow rule:
```yaml
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
```
4. **MR-specific predefined variables** to verify usage:
- `$CI_MERGE_REQUEST_IID` — short ID (e.g., 42), local to project
- `$CI_MERGE_REQUEST_ID` — global ID
- `$CI_MERGE_REQUEST_TARGET_BRANCH_NAME` — destination
- `$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME` — source
- `$CI_MERGE_REQUEST_SOURCE_PROJECT_PATH` — important for forks
- `$CI_MERGE_REQUEST_TITLE`, `$CI_MERGE_REQUEST_LABELS`
- `$CI_COMMIT_BRANCH` is EMPTY on detached MR pipelines (a common trap)
5. **For "couldn't be merged" with green pipeline**:
- Merged-results pipeline differs from MR pipeline — required pipeline status may be the merged-results one
- Approvals or status checks blocking
- Source branch out of date with target — needs rebase
- "Pipelines must succeed before merge" enabled and most-recent pipeline is queued/running
6. **For merge train failures**:
- Trains require linear, fast-forward-able state per member
- Train item ahead failed → all behind are re-tested with new merge
- Long jobs make trains slow; consider `interruptible: true` (carefully — not for deploys)
7. **For fork MRs (external contributors)**:
- Forks DON'T have access to the parent project's CI/CD variables (security!)
- Protected variables especially won't be available
- Some jobs (deploy, integration tests) need fork-pipeline allowed manually
8. **Job-level checks**:
- `rules:` matching `$CI_PIPELINE_SOURCE == "merge_request_event"` will skip on branch pipelines
- `rules:` matching `$CI_COMMIT_BRANCH == "main"` will skip on MR pipelines (because `$CI_COMMIT_BRANCH` is empty)
9. Mark anything DESTRUCTIVE: editing workflow rules during an active merge train, disabling merged-results mid-batch.
---
MR ID + target branch: [DESCRIBE]
GitLab tier: [Free / Premium / Ultimate]
MR pipeline settings:
- Pipelines for MRs: [enabled / disabled]
- Merged results pipelines: [enabled / disabled]
- Merge trains: [enabled / disabled]
Symptom: [DESCRIBE]
`workflow:rules:` (if present):
```yaml
[PASTE]
```
Relevant job's `rules:`:
```yaml
[PASTE]
```
Pipeline outcome (UI text):
[DESCRIBE]
Why this prompt works
MR pipelines have multiple types, settings, and gotchas. “Pipeline didn’t run on my MR” can be: workflow rules, rules excluding every job, project setting off, fork without permissions, or protected-branch quirk. This prompt forces a type-first diagnosis.
How to use it
- State the pipeline type GitLab actually ran (or didn’t). It’s labeled in the UI as “branch,” “detached,” or “merged result.”
- Include project settings: which MR pipeline features are enabled. Toggle settings interact.
- For fork MRs, mention “this is from a fork” — diagnostic changes (no secrets, manual run sometimes required).
- Capture
workflow:rules:— most MR pipeline issues live here.
Pipeline-type cheatsheet
| Type | $CI_PIPELINE_SOURCE | $CI_COMMIT_BRANCH | Use case |
|---|---|---|---|
| Branch | push | Set | Normal pushes |
| Detached MR | merge_request_event | EMPTY | MR-specific tests |
| Merged results | merge_request_event | EMPTY | Test “post-merge” state (Premium+) |
| Merge train | merge_request_event | EMPTY | Serialized merge testing (Premium+) |
| Schedule | schedule | Set | Cron-style |
| Manual (Run pipeline) | web | Set | One-off |
| API trigger | api, trigger | Set / Empty | Pipeline orchestration |
Useful API queries
# Get pipelines for an MR
curl --header "PRIVATE-TOKEN: <t>" \
"https://gitlab.example.com/api/v4/projects/<id>/merge_requests/<iid>/pipelines" | jq
# Detailed pipeline including source
curl --header "PRIVATE-TOKEN: <t>" \
"https://gitlab.example.com/api/v4/projects/<id>/pipelines/<pid>" | jq
# Variables available at the pipeline
curl --header "PRIVATE-TOKEN: <t>" \
"https://gitlab.example.com/api/v4/projects/<id>/pipelines/<pid>/variables" | jq
# Merge status (UI checks)
curl --header "PRIVATE-TOKEN: <t>" \
"https://gitlab.example.com/api/v4/projects/<id>/merge_requests/<iid>?include_diverged_commits_count=true&include_rebase_in_progress=true" | jq
Workflow patterns
Standard: avoid duplicate pipelines
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
Result: MR push runs only the MR pipeline; default branch push and tags run their pipelines; other branches without MRs skip.
Test gates differ on MR vs default
test-unit:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
test-integration:
# Heavy — only on MRs targeting default branch, or on default branch
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
deploy-staging:
# Only on default branch after merge
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Skip CI on docs-only
workflow:
rules:
- if: $CI_COMMIT_MESSAGE =~ /\[skip ci\]/
when: never
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH
Common findings this catches
- “No pipeline on MR” with workflow rules that only match
$CI_COMMIT_BRANCH→$CI_COMMIT_BRANCHis empty on MR pipelines. Addmerge_request_eventsource rule. - Two pipelines per MR push → standard duplicate; apply the workflow rule above.
- Merged-results pipeline expected, only detached running → “Merged results pipelines” toggle off in project settings (Premium+).
- Merge button disabled with green pipeline → required pipeline is the merged-results one, but only detached ran. Or: approvals/status checks blocking.
- Fork MR’s deploy job has empty
$AWS_KEY→ protected variable, fork has no access. Don’t deploy from fork pipelines; require maintainer to push to a branch. - Merge train member fails over and over → fix at the front of the train; behind-the-front members re-test with each front change.
$CI_MERGE_REQUEST_IIDempty in detached pipeline → check: is this actually an MR pipeline? Or did someone trigger via API as a branch pipeline?
Merge train tips
- Use
interruptible: trueon tests,falseon deploys - Long pipelines → fewer trains/hour; consider whether train is worth it
- Restart a single failing member sometimes recovers (transient infrastructure issue) without invalidating others ahead
When to escalate
- Merge train backed up severely → coordinate; possibly disable trains temporarily and re-test.
- Settings discrepancy between group default and project override → align with platform team’s policy.
- External fork MRs from a security-sensitive contributor — likely needs manual review and an internal-branch flow, not auto-pipelines.
Related prompts
-
GitLab CI/CD `rules:` Debugging Prompt
Diagnose why a GitLab job did or didn't run — decode `rules:` evaluation, `only/except` legacy syntax, workflow rules, and complex `$CI_*` variable conditions.
-
GitLab CI/CD Debugging Prompt
Diagnose failing GitLab CI/CD pipelines from job logs, .gitlab-ci.yml, and runner configuration.
-
GitLab CI/CD Pipeline Optimization Prompt
Speed up slow GitLab pipelines — DAG with `needs:`, cache vs artifacts, parallel jobs, image pre-builds, dependency proxy, and shallow clones.