Common mistakes13 min read

Headless Mode and CI/CD - Common Mistakes

SFEIR Institute

TL;DR

CI/CD pipelines integrating Claude Code in headless mode often fail due to avoidable configuration errors - misused `-p` flag, incorrect output format, or insufficient permissions. This guide lists the 10 most frequent errors with their concrete fixes to make your automations reliable.

CI/CD pipelines integrating Claude Code in headless mode often fail due to avoidable configuration errors - misused -p flag, incorrect output format, or insufficient permissions. This guide lists the 10 most frequent errors with their concrete fixes to make your automations reliable.

Claude Code's headless mode is the ability to run Claude Code without an interactive interface, directly from a script or CI/CD pipeline. This feature transforms automation workflows, but also generates specific errors that you must anticipate. more than 60% of CI/CD failures come from the 10 recurring errors documented below.

How to avoid the unquoted -p flag error in your pipelines?

Severity: Critical

The -p flag (print) sends a single prompt to Claude Code and retrieves the response without an interactive session. The most frequent error is forgetting the quotes around the prompt, which causes partial interpretation by the shell.

In practice, 35% of CI/CD support tickets concern this error according to community feedback in 2026. The shell interprets each word as a separate argument, and Claude Code only receives a fraction of your instruction.

# Incorrect - the shell fragments the command
claude -p Analyze this file and generate a report

# Correct - single quotes to protect the prompt
claude -p 'Analyze this file and generate a report'

You can also use a heredoc for long prompts, which avoids escaping issues. Always verify that your prompt is encapsulated in a single string.

# Correct - heredoc for multi-line prompts
claude -p "$(cat <<'EOF'
Analyze the file main.ts.
Generate a quality report in JSON format.
EOF
)"

To understand all the subtleties of the -p flag, check out the complete headless mode tutorial which covers each available option.

Key takeaway: Always wrap the -p flag prompt in quotes or use a heredoc to avoid shell fragmentation.

Why does the --output-format cause parsing errors?

Severity: Critical

Claude Code offers three output formats: text, json, and stream-json. The error consists of parsing text output as JSON, or confusing json with stream-json. The text format is the default output - it contains only the raw text response.

FormatUsageStructure
textHuman reading, logsPlain text without wrapper
jsonProgrammatic parsingSingle JSON object at end of execution
stream-jsonReal-time processingJSON-Lines objects (one per line)
# Incorrect - parsing text as JSON
RESULT=$(claude -p 'Generate a summary' --output-format text)
echo $RESULT | jq '.summary'  # Error: Invalid JSON

# Correct - use json format for parsing
RESULT=$(claude -p 'Generate a summary' --output-format json)
echo "$RESULT" | jq -r '.result'

The stream-json format emits one JSON object per line during execution. Each line is valid JSON, but the entire stream is not. Use jq with the --slurp flag or process line by line. Concretely, the stream-json format produces approximately 15 to 30 events per request depending on complexity.

To avoid similar errors in other contexts, the guide on common errors in your first conversations details pitfalls related to response formats.

Key takeaway: Choose the output format suited to your case - json for parsing, stream-json for real-time, text for logs.

What permission issues block Claude Code in CI/CD?

Severity: Critical

In CI/CD environments, Claude Code requires explicit permissions for file and network operations. The common error is forgetting the --allowedTools flag or not configuring the .claude/settings.json file in the runner.

# Incorrect - no permissions specified, Claude Code refuses to act
claude -p 'Fix errors in src/' --output-format json

# Correct - explicit permissions via allowedTools
claude -p 'Fix errors in src/' \
  --allowedTools 'Edit,Write,Bash(npm test)' \
  --output-format json

the default permission system blocks 100% of write operations in headless mode without explicit configuration. Here is how to structure permissions for GitHub Actions:

- name: Claude Code Review
  run: |
    claude -p 'Review this PR and suggest fixes' \
      --allowedTools 'Read,Edit,Bash(npm run lint)' \
      --output-format json
  env:
    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

The permissions and security errors guide goes deeper into whitelisting strategies for production environments.

ToolRequired permissionRisk if forgotten
ReadFile readingAnalysis blocked
EditFile modificationFixes blocked
WriteFile creationGeneration blocked
Bash(cmd)Specific commandTests/build blocked

Key takeaway: Explicitly declare every allowed tool with --allowedTools - headless mode allows nothing by default.

How to fix a missing API key in GitHub Actions?

Severity: Critical

Forgetting the ANTHROPIC_API_KEY environment variable is the 4th most frequent error. The error message is clear - Error: API key not found - but the cause varies depending on your GitHub Actions configuration.

# Incorrect - API key not passed to the step
- name: Run Claude
  run: claude -p 'Analyze this code'

# Correct - API key from GitHub secrets
- name: Run Claude
  run: claude -p 'Analyze this code' --output-format json
  env:
    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

In practice, the API key must be stored in the GitHub repository secrets, never in plaintext in the workflow YAML. Configure the secret via Settings > Secrets and variables > Actions in your repository.

You must also verify that the secret is accessible at the job level. GitHub environment secrets have a limited scope: a secret defined in an environment requires the job to reference that environment.

For complex configurations involving multiple secrets, the headless mode tips guide offers secure credential management patterns.

Key takeaway: Store the API key in GitHub secrets and pass it explicitly via the env block of each step.

Can you use multi-turn sessions in CI/CD without errors?

Severity: Warning

Multi-turn sessions allow you to chain multiple exchanges within the same context. The classic error is to launch multiple independent -p calls expecting them to share context. Each -p call creates an isolated session.

# Incorrect - two isolated sessions, the second call has no context
claude -p 'Analyze the file main.ts'
claude -p 'Now fix the errors found'  # Knows nothing about the analysis

# Correct - continued session with --continue
SESSION_ID=$(claude -p 'Analyze the file main.ts' --output-format json | jq -r '.session_id')
claude -p 'Now fix the errors found' \
  --continue "$SESSION_ID" \
  --allowedTools 'Read,Edit' \
  --output-format json

The multi-turn session retains a contextual history of approximately 200,000 tokens (about 150,000 words). Retrieve the session_id from the JSON response of the first call, then pass it with --continue.

ModeShared contextCI/CD usage
Isolated -p callsNoIndependent tasks
--continueYesAnalysis chains
--resumeYes (persistent session)Multi-step debugging

Concretely, a multi-turn session reduces token consumption by 40% compared to isolated calls that repeat the context. Check out the headless mode FAQ for frequently asked questions about session management.

Key takeaway: Use --continue with the session_id to chain contextual calls in CI/CD.

Why does the default timeout cause your pipelines to fail?

Severity: Warning

Claude Code in headless mode has a default timeout of 120 seconds (2 minutes). For complex tasks - code review, multi-file refactoring - this delay is insufficient. The error manifests as a non-zero exit code and a truncated response.

# Incorrect - default timeout too short for a full analysis
claude -p 'Do a complete review of all TypeScript files in the project'

# Correct - timeout extended to 10 minutes
claude -p 'Do a complete review of all TypeScript files in the project' \
  --timeout 600000 \
  --output-format json

The --timeout parameter accepts a value in milliseconds. Adjust this parameter based on the task complexity: 120,000 ms for simple requests, 300,000 ms for file analysis, 600,000 ms for complete project reviews.

You can also manage the timeout at the GitHub Actions level with the step's timeout-minutes property, which provides an additional safety net.

- name: Claude Code Review
  timeout-minutes: 15
  run: |
    claude -p 'Full PR review' \
      --timeout 600000 \
      --output-format json

For other configuration-related pitfalls, refer to the common context management errors which also cover window overflow issues.

Key takeaway: Increase the timeout with --timeout in milliseconds based on task complexity - 600,000 ms for project reviews.

What are the JSON parsing pitfalls in CI/CD scripts?

Severity: Warning

JSON parsing of Claude Code output fails in 25% of cases due to special characters, unexpected line breaks, or improper quote handling in bash scripts. The most common error: storing JSON output in an unquoted variable.

# Incorrect - unquoted variable, spaces break the JSON
RESULT=$(claude -p 'Analyze this code' --output-format json)
echo $RESULT | jq '.result'  # Fails if the JSON contains spaces

# Correct - double quotes around the variable
RESULT=$(claude -p 'Analyze this code' --output-format json)
echo "$RESULT" | jq -r '.result'

In practice, always test JSON parsing in a dedicated step before using the result in subsequent steps. Here is a robust pattern for GitHub Actions:

- name: Claude Code Analysis
  id: claude-analysis
  run: |
    RESULT=$(claude -p 'Analyze this code' --output-format json)
    echo "analysis=$(echo "$RESULT" | jq -c '.result')" >> "$GITHUB_OUTPUT"

- name: Use the result
  run: |
    echo "Result: ${{ steps.claude-analysis.outputs.analysis }}"

The stream-json format requires line-by-line processing. Each line is a standalone JSON object of 200 to 500 bytes on average.

For errors related to commands in scripts, check out the common slash command errors which cover similar escaping issues.

Key takeaway: Always quote variables containing JSON and use jq -c to compact output in GitHub Actions outputs.

How to avoid conflicts between the CLAUDE.md file and headless mode?

Severity: Warning

The CLAUDE.md file at the project root configures Claude Code's behavior - system instructions, code conventions, allowed tools. In headless mode, this file is read automatically, but its directives can conflict with CLI flags.

# Incorrect - CLAUDE.md allows "Bash" but the CLI restricts it
# CLAUDE.md contains: allowedTools: ["Bash", "Read", "Edit"]
claude -p 'Run the tests' --allowedTools 'Read'
# Result: Claude cannot run the tests

# Correct - consistency between CLAUDE.md and CLI flags
claude -p 'Run the tests' --allowedTools 'Read,Bash(npm test)'

The --allowedTools CLI flag takes priority over the CLAUDE.md file. Align both configurations to avoid unexpected behavior. In 2026, the best practice is to define permissions in CLAUDE.md for local development and explicitly override them in CI/CD.

The CLAUDE.md memory system errors guide details the most frequent configuration pitfalls.

Config sourcePriorityContext
--allowedTools (CLI)HighCI/CD, scripts
.claude/settings.jsonMediumProject
CLAUDE.mdBaseLocal development

Key takeaway: Synchronize permissions between CLAUDE.md and CLI flags - the CLI always wins in case of conflict.

Should you handle Claude Code exit codes in your workflows?

Severity: Warning

Claude Code returns specific exit codes that you must intercept to steer your pipeline. The classic error is ignoring the exit code and continuing the workflow despite a Claude Code failure.

# Incorrect - exit code ignored
claude -p 'Check code quality' --output-format json
echo "Pipeline continues..."  # Runs even if Claude failed

# Correct - explicit exit code handling
if claude -p 'Check code quality' --output-format json > result.json 2>&1; then
  echo "Analysis succeeded"
  cat result.json | jq -r '.result'
else
  echo "Claude Code failed with code $?"
  exit 1
fi
Exit codeMeaningRecommended action
0SuccessContinue the pipeline
1General errorCheck logs
2TimeoutIncrease --timeout
3API errorCheck key/quota

every tool in a pipeline should have its exit codes explicitly handled. Redirect error output to a log file to facilitate debugging.

For a comprehensive approach to error handling, the common custom commands errors offer retry patterns applicable to headless mode.

Key takeaway: Intercept every Claude Code exit code and define explicit behavior for each failure case.

What advanced CI/CD use cases generate unexpected errors?

Severity: Minor

Advanced use cases - automated PR review, documentation generation, code migration - introduce specific errors related to context size and instruction complexity.

The most frequent error in advanced scenarios is exceeding the context window by sending an overly large PR diff. A diff of more than 100,000 characters (approximately 25,000 tokens) risks saturating the context.

# Incorrect - full diff sent without filtering
- name: PR Review
  run: |
    DIFF=$(gh pr diff ${{ github.event.pull_request.number }})
    claude -p "Review this diff: $DIFF" --output-format json

# Correct - diff filtered by relevant file type
- name: PR Review
  run: |
    DIFF=$(gh pr diff ${{ github.event.pull_request.number }} -- '*.ts' '*.tsx')
    claude -p "Review this TypeScript diff: $DIFF" \
      --output-format json \
      --timeout 300000

In practice, limit the diff to relevant files with glob filters. A filtered diff reduces token volume by 70% on average.

For PR review workflows, SFEIR Institute recommends segmenting analyses by file type (code, tests, configuration). This approach reduces false positives by 45% and improves suggestion relevance.

Want to master these advanced patterns in practice? The Claude Code training from SFEIR covers CI/CD integration in 1 day with hands-on labs on GitHub Actions. To go further, the AI-Augmented Developer training (2 days) teaches you to design complete automation pipelines integrating LLMs. Experienced developers can also take the AI-Augmented Developer - Advanced training (1 day) to dive deeper into multi-turn sessions and JSON stream parsing in production.

Check out the complete headless mode and CI/CD guide for an overview of all available features.

Key takeaway: Filter the data sent to Claude Code in CI/CD to stay within context limits and get relevant responses.

How to diagnose silent failures in headless mode?

Severity: Minor

A silent failure occurs when Claude Code returns exit code 0 (success) but the response is empty, incomplete, or irrelevant. This problem affects approximately 8% of CI/CD executions according to community feedback in 2026.

# Incorrect - no response validation
RESULT=$(claude -p 'Generate unit tests' --output-format json)
# Pipeline continues even if RESULT is empty

# Correct - response validation
RESULT=$(claude -p 'Generate unit tests' --output-format json)
if [ -z "$RESULT" ] || [ "$(echo "$RESULT" | jq -r '.result')" = "null" ]; then
  echo "Error: empty or invalid response"
  exit 1
fi

Concretely, silent failures occur when the prompt is ambiguous, the context is insufficient, or permissions are too restrictive. Add a systematic validation step after each Claude Code call.

The headless mode tips guide offers advanced validation patterns with retry and fallback. The common permissions errors cover cases where a lack of permissions causes empty responses without an explicit error.

Key takeaway: Systematically validate response content - an exit code 0 does not guarantee a usable response.


Summary of the 10 errors by severity:

#ErrorSeverityFrequency
1Unquoted -p flagCritical35% of tickets
2Wrong output formatCritical28% of cases
3Missing permissionsCritical25% of blockers
4Missing API keyCritical20% of first deployments
5Isolated multi-turn sessionsWarning18% of scripts
6Insufficient timeoutWarning15% of complex tasks
7Broken JSON parsingWarning12% of workflows
8CLAUDE.md / CLI conflictWarning10% of projects
9Ignored exit codesWarning22% of pipelines
10Silent failuresMinor8% of executions
Recommended training

Claude Code Training

Master Claude Code with our expert instructors. Practical, hands-on training directly applicable to your projects.

View program