How to fix Error: The process '/usr/bin/git' failed with exit code 128 in Git

GitINTERMEDIATEHIGHUpdated Dec 6, 2025· 267 views

This error occurs in GitHub Actions when the actions/checkout step fails to clone or access a repository. Exit code 128 indicates a fatal Git error, typically caused by authentication issues, missing repository permissions, or invalid references.

Quick fix

Fixes 128

name: CI
on: [push, pull_request]

permissions:
  contents: read  # Required for checkout

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
  1. Check repository permissions and token scopes
  2. Verify the ref (branch/tag/SHA) exists
  3. Configure submodule authentication
  4. Handle fork pull requests correctly
git — error
name: CI
on: [push, pull_request]
permissions:
contents: read # Required for checkout
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Error: The process '/usr/bin/git' failed with exit code 128

What this error means

Exit code 128 in Git is a catch-all for "fatal" errors - situations where Git cannot proceed with the requested operation. When this appears in GitHub Actions, it means the `actions/checkout` action (or a manual `git` command) encountered an unrecoverable error. The error occurs during the repository checkout phase of your workflow, before your actual build or test steps run. GitHub Actions uses a special token (GITHUB_TOKEN) to authenticate with repositories, and this token has specific permissions and limitations. Common scenarios that trigger exit code 128: 1. **Authentication failure** - The workflow doesn't have permission to access the repository 2. **Repository not found** - The repo doesn't exist, is private without proper access, or the reference is wrong 3. **Invalid reference** - Trying to checkout a branch, tag, or commit that doesn't exist 4. **Submodule issues** - Submodules require additional authentication configuration 5. **Shallow clone conflicts** - Operations that require full history on a shallow clone 6. **LFS problems** - Large File Storage files can't be fetched due to bandwidth or authentication

Typical symptoms

  • GitHub Actions workflow fails at the 'Checkout' step
  • Error message shows 'fatal: could not read Username' or 'fatal: repository not found'
  • Workflow worked previously but suddenly started failing
  • Checkout works on public repos but fails on private ones
  • Submodule checkout fails even though main repo succeeds
  • Workflow fails when triggered by pull_request from a fork
  • Error mentions 'reference is not a tree' or 'couldn't find remote ref'
  • LFS-tracked files fail to download during checkout

Common causes

  1. GITHUB_TOKEN lacks permissions to access private repositories or submodules
  2. Attempting to checkout a branch or tag that doesn't exist
  3. Repository was deleted, renamed, or made private
  4. Workflow triggered from a fork without proper permissions
  5. Submodules require SSH or separate authentication tokens
  6. Git LFS bandwidth exceeded or not properly configured
  7. Using deprecated checkout action version (v1 or v2) with incompatible features
  8. Repository requires SAML SSO authentication not configured in token
  9. Trying to access organization private repos with default GITHUB_TOKEN
  10. Specifying an invalid or non-existent commit SHA

How to fix "Error: The process '/usr/bin/git' failed with exit code 128"

1Check repository permissions and token scopes

The default GITHUB_TOKEN has limited permissions. For private repos or cross-repo access, you need to configure explicit permissions:

Add permissions to your workflow:

yaml
name: CI
on: [push, pull_request]

permissions:
  contents: read  # Required for checkout

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

For accessing other private repositories:

yaml
permissions:
  contents: read
  packages: read  # If accessing GitHub Packages

steps:
  - uses: actions/checkout@v4
    with:
      token: ${{ secrets.PAT_TOKEN }}  # Personal Access Token with repo scope

Create a Personal Access Token (PAT):
1. Go to GitHub Settings > Developer settings > Personal access tokens > Fine-grained tokens
2. Select the repositories you need access to
3. Grant "Contents: Read" permission
4. Add the token as a repository secret (Settings > Secrets > Actions)

2Verify the ref (branch/tag/SHA) exists

Exit code 128 often occurs when trying to checkout a non-existent reference:

Check your checkout configuration:

yaml
# Incorrect - branch might not exist
- uses: actions/checkout@v4
  with:
    ref: feature/nonexistent-branch  # This will fail if branch doesn't exist

# Correct - use default or verify branch exists
- uses: actions/checkout@v4  # Checks out the triggering ref

# Or explicitly check the correct branch
- uses: actions/checkout@v4
  with:
    ref: main

For pull requests, use the correct ref:

yaml
# For PR workflows - checkout the PR merge commit
- uses: actions/checkout@v4
  with:
    ref: ${{ github.event.pull_request.head.sha }}

# Or the PR branch
- uses: actions/checkout@v4
  with:
    ref: ${{ github.head_ref }}

Debug by printing the ref:

yaml
- name: Debug ref
  run: |
    echo "github.ref: ${{ github.ref }}"
    echo "github.sha: ${{ github.sha }}"
    echo "github.head_ref: ${{ github.head_ref }}"
3Configure submodule authentication

Submodules are a common cause of exit code 128 because they require separate authentication:

Enable submodule checkout:

yaml
- uses: actions/checkout@v4
  with:
    submodules: true  # or 'recursive' for nested submodules
    token: ${{ secrets.PAT_TOKEN }}  # PAT with access to submodule repos

For submodules using SSH URLs:

yaml
- uses: actions/checkout@v4
  with:
    submodules: true
    ssh-key: ${{ secrets.SUBMODULE_SSH_KEY }}

Generate SSH key for submodules:
1. Generate a key: ssh-keygen -t ed25519 -C "github-actions"
2. Add the public key as a Deploy Key to each submodule repository
3. Add the private key as a secret in the main repository

Convert SSH to HTTPS URLs in workflow:

yaml
- uses: actions/checkout@v4
- name: Convert submodule URLs
  run: |
    git config --global url."https://github.com/".insteadOf "git@github.com:"
    git submodule update --init --recursive
  env:
    GH_TOKEN: ${{ secrets.PAT_TOKEN }}
4Handle fork pull requests correctly

Pull requests from forks have limited access to secrets and permissions by default:

For public repositories:

yaml
on:
  pull_request_target:  # Runs in the context of the base repo
    types: [opened, synchronize]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}

Warning about pull_request_target:
Using pull_request_target runs workflow code from the base branch with access to secrets. Never run untrusted code from the fork in this context.

Safer approach - split workflows:

yaml
# For safe operations (checkout, build)
on: pull_request

# For operations needing secrets (deploy, comment)
on:
  workflow_run:
    workflows: ["Build"]
    types: [completed]

Check if running from fork:

yaml
- name: Check if fork
  run: |
    if [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then
      echo "This is a fork PR"
    fi
5Update to latest actions/checkout version

Older versions of actions/checkout have known bugs and missing features:

Use the latest version (v4):

yaml
# Old - may have issues
- uses: actions/checkout@v2

# Current - recommended
- uses: actions/checkout@v4

Version differences:
- v1: Deprecated, uses deprecated Git protocols
- v2: Works but lacks some features
- v3: Node 16, good compatibility
- v4: Node 20, latest features and fixes

Common issues fixed in newer versions:
- Better handling of sparse checkout
- Improved submodule authentication
- Fixed issues with detached HEAD
- Better performance with partial clones

Pin to specific version for stability:

yaml
# Pin to exact version
- uses: actions/checkout@v4.1.1

# Or use major version (recommended)
- uses: actions/checkout@v4
6Fix Git LFS issues

Large File Storage (LFS) files can cause checkout failures:

Enable LFS in checkout:

yaml
- uses: actions/checkout@v4
  with:
    lfs: true

If LFS quota exceeded:

yaml
# Skip LFS files if not needed
- uses: actions/checkout@v4
  with:
    lfs: false

# Or fetch only specific LFS files
- uses: actions/checkout@v4
- name: Fetch specific LFS files
  run: |
    git lfs pull --include="path/to/needed/files/*"

Check LFS status:

yaml
- uses: actions/checkout@v4
  with:
    lfs: true
- name: Verify LFS
  run: |
    git lfs ls-files
    git lfs status

LFS bandwidth issues:
- GitHub Free: 1 GB storage, 1 GB bandwidth/month
- Consider caching LFS files between runs
- Or host large files elsewhere

7Increase fetch depth for operations requiring history

By default, actions/checkout performs a shallow clone (depth=1). Some operations need more history:

Fetch full history:

yaml
- uses: actions/checkout@v4
  with:
    fetch-depth: 0  # Fetch all history for all branches and tags

Fetch specific depth:

yaml
# Fetch last 10 commits
- uses: actions/checkout@v4
  with:
    fetch-depth: 10

When you need full history:
- Running git describe
- Generating changelogs
- Operations that traverse commit history
- Some version detection tools

Fetch specific refs:

yaml
- uses: actions/checkout@v4
  with:
    fetch-depth: 0
    fetch-tags: true  # Ensure tags are fetched

Partial clone for large repos:

yaml
- uses: actions/checkout@v4
  with:
    filter: blob:none  # Don't fetch file contents initially
8Debug with verbose Git output

Add debugging to understand exactly what's failing:

Enable Git trace logging:

yaml
- uses: actions/checkout@v4
  env:
    GIT_TRACE: 1
    GIT_CURL_VERBOSE: 1
    GIT_TRACE_PACKET: 1

# Or for more detail
- name: Debug checkout
  run: |
    GIT_TRACE=2 GIT_CURL_VERBOSE=1 git clone https://github.com/${{ github.repository }}.git
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Check token permissions:

yaml
- name: Check token scopes
  run: |
    curl -sS -f -I -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" https://api.github.com | grep -i x-oauth-scopes

List available refs:

yaml
- name: List refs
  run: |
    git ls-remote https://github.com/${{ github.repository }}.git
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Manual clone to isolate issue:

yaml
- name: Manual clone
  run: |
    git clone --depth 1 https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git repo
    cd repo && git status

Advanced notes

### Understanding GITHUB_TOKEN Permissions

The default GITHUB_TOKEN in GitHub Actions has specific permission boundaries:

Same repository: Full read/write access (configurable)
Other repositories: No access by default
Forks (pull_request event): Read-only, no secrets access

yaml
# Explicit permissions (recommended)
permissions:
  actions: read
  contents: read
  issues: write
  pull-requests: write

### Organization-Wide Private Repositories

For accessing multiple private repos in an organization:

1. Create a GitHub App with necessary permissions
2. Install the App in your organization
3. Generate installation tokens in workflows:

yaml
- uses: tibdex/github-app-token@v1
  id: app-token
  with:
    app_id: ${{ secrets.APP_ID }}
    private_key: ${{ secrets.APP_PRIVATE_KEY }}

- uses: actions/checkout@v4
  with:
    token: ${{ steps.app-token.outputs.token }}
    repository: org/private-repo

### SAML SSO Requirements

If your organization uses SAML SSO:

1. Personal Access Tokens must be authorized for SSO
2. Go to token settings and click "Authorize" next to your organization
3. GitHub Apps bypass SSO requirements

### Checkout Performance Optimization

For large repositories:

yaml
- uses: actions/checkout@v4
  with:
    fetch-depth: 1
    filter: blob:none
    sparse-checkout: |
      src/
      package.json
    sparse-checkout-cone-mode: true

### Self-Hosted Runners

On self-hosted runners, Git configuration persists:

yaml
- uses: actions/checkout@v4
  with:
    clean: true  # Ensure clean working directory
    persist-credentials: false  # Don't store credentials

### Common Exit Codes Reference

| Exit Code | Meaning |
|-----------|---------|
| 1 | Generic error |
| 128 | Fatal error (auth, not found, invalid ref) |
| 129 | Invalid options |

### Git Protocol Selection

Force HTTPS instead of SSH for submodules:

yaml
- name: Configure Git to use HTTPS
  run: |
    git config --global url."https://github.com/".insteadOf git@github.com:
    git config --global url."https://github.com/".insteadOf ssh://git@github.com/

Frequently asked questions

What does "128" mean?
Exit code 128 in Git is a catch-all for "fatal" errors - situations where Git cannot proceed with the requested operation. When this appears in GitHub Actions, it means the actions/checkout action (or a manual git command) encountered an unrecoverable error. The error occurs during the repository checkout phase of your workflow, before your actual build or test steps run.
How do I fix "128"?
This error occurs in GitHub Actions when the actions/checkout step fails to clone or access a repository. Exit code 128 indicates a fatal Git error, typically caused by authentication issues, missing repository permissions, or invalid references.
What causes "128"?
GITHUB_TOKEN lacks permissions to access private repositories or submodules Attempting to checkout a branch or tag that doesn't exist Repository was deleted, renamed, or made private
Is the "128" error serious?
Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.

Official resources & further reading