Fix: Error: Process completed with exit code 1 (GitHub Actions)

The Error

Your GitHub Actions workflow fails and you see:

Error: Process completed with exit code 1

Or one of these variations:

Error: Process completed with exit code 2
Error: Process completed with exit code 127
Error: Process completed with exit code 128
##[error]The process '/usr/bin/docker' failed with exit code 1
Error: Action failed with "The template is not valid."

The workflow run shows a red X, and the step that failed is collapsed in the logs. You click it, scroll through dozens of lines, and still can’t figure out what went wrong.

Why This Happens

“Process completed with exit code 1” is not the error itself. It’s GitHub Actions telling you that a command in your workflow returned a non-zero exit code. The actual error is somewhere in the log output above that line.

Every step in a GitHub Actions workflow runs a shell command (or a JavaScript/Docker action). When that command exits with code 0, it means success. Any other code means failure:

Exit CodeMeaning
0Success
1General error (most common)
2Misuse of shell command / invalid arguments
127Command not found
128Invalid exit argument
137Process killed (OOM or timeout — SIGKILL)
139Segmentation fault (SIGSEGV)

The root cause is always in the logs above the exit code message. Here’s how to find it.

Fix 1: Read the Logs Properly

This sounds obvious, but most people don’t read the logs carefully enough. Here’s the efficient way:

  1. Go to the Actions tab in your repository.
  2. Click the failed workflow run.
  3. Click the failed job (the one with the red X).
  4. Click the failed step to expand it.
  5. Scroll up from the exit code message. The actual error is above the “Process completed with exit code 1” line, not below it.

Look for lines containing:

  • error, Error, ERROR
  • fatal
  • FAILED
  • not found
  • permission denied
  • No such file or directory

Pro tip: Click the gear icon at the top of the log viewer and enable Show timestamps. This helps correlate errors with specific commands when a step runs multiple commands.

Pro tip 2: Add set -x at the top of a multi-line run: block to see exactly which command failed:

- run: |
    set -x
    npm ci
    npm run build
    npm test

This prints each command before executing it, so you can see which one triggered the failure.

Fix 2: Fix Node.js Step Failures

If your workflow runs npm ci, npm run build, or npm test and fails, the error is almost always in the build or test output, not in GitHub Actions itself.

npm ci fails

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree

Your package-lock.json is out of sync with package.json. Run npm install locally, commit the updated lockfile, and push.

If your project uses a specific Node.js version, make sure the workflow matches:

- uses: actions/setup-node@v4
  with:
    node-version: '20'

A version mismatch between your local machine and CI is one of the most common causes of “works on my machine” failures. If you’re seeing module errors, check Fix: Cannot find module in Node.js.

npm run build fails

The build might succeed locally because of case-insensitive filesystems. macOS and Windows are case-insensitive; Linux (which GitHub Actions runners use) is case-sensitive:

Module not found: Can't resolve './Components/Header'
# The actual file is ./components/Header.tsx (lowercase 'c')

Fix the import casing to match the actual filename exactly.

npm test fails

Tests may depend on environment variables, databases, or services that exist locally but not in CI. Check if your tests need a .env file or a running database. See Fix: process.env.VARIABLE_NAME is undefined for environment variable issues.

Fix 3: Fix Python Step Failures

pip install fails

ERROR: Could not find a version that satisfies the requirement somepackage==1.2.3

The package version doesn’t exist or was yanked from PyPI. Check requirements.txt for typos or pinned versions that no longer exist.

- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
- run: pip install -r requirements.txt

pytest fails

Same as Node.js tests — look for the actual assertion error or import error in the output. Python tracebacks print the error at the bottom, so scroll to the end of the traceback.

Missing system dependencies

Some Python packages need system libraries to compile:

- run: |
    sudo apt-get update
    sudo apt-get install -y libpq-dev gcc
    pip install -r requirements.txt

Fix 4: Fix Docker Step Failures

Docker build fails

##[error]The process '/usr/bin/docker' failed with exit code 1

Expand the step and look for the actual Docker build error. Common causes:

  • COPY failed: file not found. The file path in your Dockerfile doesn’t match the actual file structure. Paths are relative to the build context (usually the repo root).
  • RUN command failed. A RUN command in your Dockerfile returned non-zero. Read the output of that specific RUN step.
  • Base image not found. The FROM image doesn’t exist or requires authentication.

Docker login fails

- uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: ${{ github.actor }}
    password: ${{ secrets.GITHUB_TOKEN }}

Make sure you’re using secrets.GITHUB_TOKEN (not a custom PAT that expired) and that the repository has package write permissions enabled.

Fix 5: Fix Secrets and Environment Variables

Secret is empty or not set

Error: Input required and not supplied: token

Secrets are not available in:

  • Workflows triggered by pull requests from forks. This is a security feature. Forked PRs cannot access your repository secrets.
  • Workflows that reference the wrong secret name. Secret names are case-sensitive. secrets.API_KEY and secrets.api_key are different.
  • Reusable workflows unless you explicitly pass them with the secrets keyword.

Check your secrets in Settings > Secrets and variables > Actions. Verify the name matches exactly what your workflow references:

env:
  API_KEY: ${{ secrets.API_KEY }}

Secret expired or was rotated

If a workflow that previously worked suddenly fails, check if the secret (API key, deploy token, SSH key) has expired. Re-create it and update the secret in your repository settings.

Environment variables not available across steps

Each run: step starts a new shell. Variables set in one step are not available in the next:

# WRONG -- $MY_VAR is empty in the second step
- run: export MY_VAR="hello"
- run: echo $MY_VAR

# CORRECT -- use GITHUB_ENV
- run: echo "MY_VAR=hello" >> $GITHUB_ENV
- run: echo $MY_VAR  # "hello"

For environment variables in general, see Fix: process.env.VARIABLE_NAME is undefined.

Fix 6: Fix GITHUB_TOKEN Permission Errors

Error: Resource not accessible by integration
fatal: could not read Username for 'https://github.com': terminal prompts disabled

The default GITHUB_TOKEN has limited permissions. If your workflow needs to push commits, create releases, or write to packages, you need to explicitly grant permissions:

permissions:
  contents: write
  packages: write
  pull-requests: write

Add this at the job level or workflow level:

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      packages: write
    steps:
      - uses: actions/checkout@v4
      # ...

Important: When you set permissions explicitly, you only get the permissions you list. All others default to none. If you only set contents: write, you lose the default read access to other scopes.

Common permission requirements:

ActionRequired Permission
Push commitscontents: write
Create/update PRspull-requests: write
Push Docker images to GHCRpackages: write
Deploy to GitHub Pagespages: write, id-token: write
Comment on issuesissues: write
Read private repos (submodules)contents: read on the target repo

Fix 7: Fix Checkout Issues

Shallow clone missing commits

fatal: couldn't find remote ref

By default, actions/checkout does a shallow clone (depth 1). If your build needs the full Git history (for changelogs, version calculation, or git diff):

- uses: actions/checkout@v4
  with:
    fetch-depth: 0  # full history

Submodules not checked out

fatal: No url found for submodule path 'some/submodule'
- uses: actions/checkout@v4
  with:
    submodules: recursive
    token: ${{ secrets.PAT_TOKEN }}  # if submodules are private repos

The default GITHUB_TOKEN only has access to the repository the workflow runs in. For private submodules, you need a Personal Access Token (PAT) with access to those repositories.

Wrong branch checked out

In pull request workflows, actions/checkout checks out the merge commit by default. If you need the PR head branch:

- uses: actions/checkout@v4
  with:
    ref: ${{ github.event.pull_request.head.sha }}

Fix 8: Fix Caching Failures

Cache miss every time

Cache not found for input keys: ...

The cache key doesn’t match any existing cache. Common mistakes:

  • Using a key that changes every run (like a timestamp or commit SHA). Use a key based on your lockfile hash:
- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      npm-${{ runner.os }}-
  • Caching the wrong path. node_modules is not a good cache target (it’s platform-dependent). Cache the npm/yarn/pnpm cache directory instead.
  • Cache eviction. GitHub evicts caches not accessed in 7 days or when the 10 GB limit per repo is hit. Old caches from stale branches fill up the limit.

Cache restore succeeds but build still fails

The cache might be corrupted or stale. Clear it:

gh cache delete --all

Or change the cache key to force a fresh cache.

Fix 9: Fix Timeout Failures

Error: The operation was canceled.
##[error]The runner has received a shutdown signal.

Job timeout

The default timeout for a job is 360 minutes (6 hours). If your job takes too long, set a shorter timeout so you get faster feedback:

jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      # ...

Step timeout

Individual steps can also hang. Set a per-step timeout:

- run: npm test
  timeout-minutes: 10

Hung processes

If a step hangs (waiting for user input, a server that never responds, an infinite loop), the job will run until the timeout. Common culprits:

  • Interactive prompts. A CLI tool asks for confirmation. Add -y or --yes flags, or set CI=true in the environment.
  • A server started without backgrounding. npm start in a workflow step will block forever. Background it or use a tool designed for CI.
  • Tests waiting for a database connection. The database service isn’t ready yet. Add a health check wait loop.

Fix 10: Fix Self-Hosted Runner Issues

Runner is offline

Error: The self-hosted runner: [name] is offline.

The runner service isn’t running, or it lost network connectivity. SSH into the runner machine and check:

cd /path/to/actions-runner
./svc.sh status
./svc.sh start

Missing tools on self-hosted runners

Unlike GitHub-hosted runners, self-hosted runners don’t come with pre-installed tools. If your workflow uses actions/setup-node or actions/setup-python, these actions install the tools, but direct commands like node, docker, or python must already be on the PATH.

Install missing tools on the runner or add setup steps to your workflow.

Working directory not clean

Self-hosted runners persist the working directory between runs. Stale files from a previous run can cause failures:

- uses: actions/checkout@v4
  with:
    clean: true  # default is true, but verify

Or add a cleanup step:

- run: git clean -ffdx

Fix 11: Fix Matrix Strategy Failures

Error: Process completed with exit code 1

With a matrix strategy, one failing combination kills the entire matrix by default. To let other combinations continue:

strategy:
  fail-fast: false
  matrix:
    node-version: [18, 20, 22]
    os: [ubuntu-latest, windows-latest]

If only one matrix combination fails, the issue is likely platform-specific or version-specific. Check the logs for that specific combination.

Common matrix pitfalls

  • Path separators. Windows uses \, Linux uses /. Use path.join() or path.resolve() in Node.js instead of hardcoded paths.
  • Shell differences. Windows runners use PowerShell by default, not bash. Force bash if your commands need it:
- run: echo "hello"
  shell: bash
  • Case sensitivity. File imports that work on Windows/macOS fail on Linux because Linux filesystems are case-sensitive.

Fix 12: Fix Artifact Upload/Download Failures

Upload fails

Error: Artifact upload failed. Please try again.
Warning: No files were found with the provided path

The path you specified doesn’t match any files. Paths are relative to the workspace root ($GITHUB_WORKSPACE):

- uses: actions/upload-artifact@v4
  with:
    name: build-output
    path: dist/  # relative to repo root
    if-no-files-found: error  # fail explicitly if no files found

If the build step creates output in a different directory, verify the path:

- run: ls -la dist/  # debug: check if files exist before upload
- uses: actions/upload-artifact@v4
  with:
    name: build-output
    path: dist/

Download fails in a different workflow

Artifacts are scoped to a workflow run. You cannot download artifacts from a different workflow run using actions/download-artifact directly. Use the GitHub API or gh run download instead.

Artifact size too large

GitHub limits artifact storage. Compress before uploading:

- run: tar -czf build.tar.gz dist/
- uses: actions/upload-artifact@v4
  with:
    name: build-output
    path: build.tar.gz
    retention-days: 5

Fix 13: Use Conditional Steps to Debug

When a step fails, subsequent steps are skipped by default. Use if: always() to run cleanup or debug steps regardless:

- run: npm test
  continue-on-error: true

- run: cat test-results.xml
  if: always()

- uses: actions/upload-artifact@v4
  if: always()
  with:
    name: test-results
    path: test-results.xml

Other useful conditionals:

# Run only on failure
- run: docker logs my-container
  if: failure()

# Run only on success
- run: echo "All tests passed"
  if: success()

# Run only on the main branch
- run: npm run deploy
  if: github.ref == 'refs/heads/main'

Fix 14: Fix YAML Syntax Errors

Error: Invalid workflow file: .github/workflows/ci.yml
The workflow is not valid. .github/workflows/ci.yml (Line X, Col Y): ...

Your workflow YAML has a syntax error. Common mistakes:

  • Incorrect indentation. YAML uses spaces, not tabs. Every level is 2 spaces:
# WRONG
jobs:
  build:
      runs-on: ubuntu-latest
      steps:
          - uses: actions/checkout@v4

# CORRECT
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
  • Missing quotes around expressions. Strings containing { or special characters need quoting:
# WRONG -- YAML parser breaks on the colon
run: echo Status: ${{ job.status }}

# CORRECT
run: echo "Status: ${{ job.status }}"
  • Multiline strings done wrong:
# Use | for literal multiline
run: |
  echo "line 1"
  echo "line 2"

For more YAML errors, see Fix: YAML mapping values are not allowed here.

Still Not Working?

The set -e behavior in shell steps

GitHub Actions runs each run: step with set -e by default (for bash). This means the step fails on the first command that returns non-zero, even if subsequent commands would have fixed the state. This catches you when you write:

- run: |
    grep "pattern" file.txt  # exit code 1 if no match -- kills the step
    echo "continuing..."     # never runs

Fix it by handling expected non-zero exits:

- run: |
    grep "pattern" file.txt || true
    echo "continuing..."

Or for a command where you want to check the exit code:

- run: |
    if grep -q "pattern" file.txt; then
      echo "Found"
    else
      echo "Not found"
    fi

Workflow runs on the wrong trigger

Your workflow might be triggering on pushes to all branches when you only want main, or not triggering on PRs at all:

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

Check the on: section carefully. A missing branches filter means the workflow runs on every push to every branch.

Actions version pinning issues

If you use @v4 or @main, the action’s code can change under you. Pin to a specific commit SHA for stability:

# Instead of:
- uses: actions/checkout@v4

# Pin to a specific SHA:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11  # v4.1.1

This also protects against supply chain attacks on third-party actions.

Rate limiting and API failures

GitHub API calls in your workflow may hit rate limits, especially in workflows that run frequently:

Error: API rate limit exceeded for installation

Reduce API calls, add retries with backoff, or use a GitHub App token with higher rate limits instead of the default GITHUB_TOKEN.

Runner disk space full

GitHub-hosted runners have limited disk space (~14 GB free). Large builds, Docker images, or cached dependencies can fill it:

- run: df -h  # check disk space

# Free up space if needed
- run: |
    sudo rm -rf /usr/share/dotnet
    sudo rm -rf /opt/ghc
    sudo rm -rf /usr/local/share/boost
    df -h

Services container not ready

If your workflow uses service containers (like PostgreSQL or Redis), they may not be ready when your tests start:

services:
  postgres:
    image: postgres:16
    env:
      POSTGRES_PASSWORD: postgres
    options: >-
      --health-cmd pg_isready
      --health-interval 10s
      --health-timeout 5s
      --health-retries 5
    ports:
      - 5432:5432

The --health-cmd option tells GitHub Actions to wait until the service is healthy before starting your steps. Without it, your tests may fail with connection errors.

Concurrency conflicts

Multiple workflow runs on the same branch can interfere with deployments. Use concurrency groups to cancel or queue runs:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

This cancels the previous run when a new push arrives, saving runner minutes and avoiding conflicting deployments.

Enable debug logging

If you still can’t figure out the error, enable debug logging by adding this secret to your repository:

  • Name: ACTIONS_RUNNER_DEBUG
  • Value: true

And for step-level debug logs:

  • Name: ACTIONS_STEP_DEBUG
  • Value: true

This produces verbose output on the next workflow run. Remove these secrets when you’re done debugging — they generate a lot of noise.


Related: Fix: YAML mapping values are not allowed here | Fix: process.env.VARIABLE_NAME is undefined | Fix: bash permission denied | Fix: Docker Permission Denied While Trying to Connect to the Docker Daemon Socket | Fix: Cannot find module in Node.js | Fix: npm ERR! Missing script: “start”

Related Articles