Jenkins API calls fail - CSRF crumb missing or expired
POST to Jenkins API returns 403 with 'No valid crumb was included in the request'
Focused incident mode — strips away everything but this runbook.
Symptoms
- POST to Jenkins API returns 403 with 'No valid crumb was included in the request'
- curl-based Jenkins API calls fail with CSRF validation error
- Jenkins CLI commands fail with '403 Forbidden' on POST requests
Always fetch a fresh crumb immediately before making a POST request. Stale crumbs expire silently and cause 403 errors that are hard to diagnose.
Evidence to grab
Collect these artifacts before you change anything — they prove (or rule out) each cause.
Jenkins CSRF configuration
curl -s -u <USER>:<TOKEN> '<JENKINS_URL>/manage' | grep -i 'crumb'Crumb issuer endpoint
curl -s -u <USER>:<TOKEN> '<JENKINS_URL>/crumbIssuer/api/json'
Diagnostic flow
Run the steps in order, marking each claim confirmed or refuted. The evidence you gather feeds the ranked causes below.
Guided decision tree is ready.
Steps with commands, expected output, and pass/fail branches.
Ranked root causes
Evidence checklist
- POST request does not include the Jenkins-Crumb header
- Jenkins CSRF protection is enabled (default since Jenkins 2.0)
Confirm before fixing
curl -s -u <USER>:<TOKEN> '<JENKINS_URL>/crumbIssuer/api/json'Fetch the crumb
Fix
CRUMB=$(curl -s -u <USER>:<TOKEN> '<JENKINS_URL>/crumbIssuer/api/json' | python3 -c 'import sys,json; print(json.load(sys.stdin)["crumb"])') && curl -s -u <USER>:<TOKEN> -H 'Jenkins-Crumb: '$CRUMB -X POST '<JENKINS_URL>/job/<JOB>/build'Include the crumb in the request header
Resolution runbook
Apply from top to bottom after the cause is confirmed. Review every command before running it — risk tags are shown on each.
curl -s -u <USER>:<TOKEN> '<JENKINS_URL>/crumbIssuer/api/json'Fetch a fresh crumb
CRUMB=$(curl -s -u <USER>:<TOKEN> '<JENKINS_URL>/crumbIssuer/api/json' | python3 -c 'import sys,json; print(json.load(sys.stdin)["crumb"])')Extract the crumb value
curl -s -u <USER>:<TOKEN> -H 'Jenkins-Crumb:'$CRUMB -X POST '<JENKINS_URL>/job/<JOB>/build'Trigger a build with the crumb
Prevention
- Include the crumb fetch in a single shell command chain: CRUMB=$(curl ... | jq -r .crumb) && curl -H 'Jenkins-Crumb:'$CRUMB ...
- Use Jenkins API tokens instead of passwords for scripted API access.
- Document the crumb requirement in your Jenkins API usage guides.
- Consider using the Jenkins CLI for complex API operations - it handles crumb fetching automatically.
Related runbooks
Same stack, same failure family — worth a skim.
Build fails with 'Workspace is locked by another build'
Jenkins master starts and crashes repeatedly within seconds
Commands marked READ CHANGE DESTRUCTIVE — never run DESTRUCTIVE commands against production data you cannot restore. Copy → verify → execute.