# How to Troubleshoot Actions/Checkout Errors: A Complete Guide to the GitHub Action Source Code

> Troubleshoot actions checkout errors with step debug logging. Trace failures through source code like git-auth-helper.ts and git-command-manager.ts to resolve token scope, fetch depth, or submodule issues.

- Repository: [GitHub Actions/checkout](https://github.com/actions/checkout)
- Tags: how-to-guide
- Published: 2026-07-17

---

**Enable step debug logging with `ACTIONS_STEP_DEBUG` to expose the exact Git commands and authentication headers that `actions/checkout` executes, then trace failures through the specific helper files ([`git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/git-auth-helper.ts), [`git-command-manager.ts`](https://github.com/actions/checkout/blob/main/git-command-manager.ts)) to identify whether the issue stems from token scopes, fetch depth, or submodule configuration.**

The `actions/checkout` GitHub Action is the official mechanism for cloning repositories into workflow runners. When checkout steps fail with cryptic Git errors, understanding the internal architecture—from input parsing in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts) to authentication handling in [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts)—allows you to diagnose root causes systematically rather than guessing at configuration fixes.

## Understanding the Checkout Architecture

The action follows a strict pipeline defined in [`src/main.ts`](https://github.com/actions/checkout/blob/main/src/main.ts). First, `input-helper.getInputs()` reads all parameters (`ref`, `token`, `ssh-key`, `fetch-depth`, `submodules`) and logs resolved values via `core.debug` (line 18 of [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts)). Next, the action registers a problem matcher ([`problem-matcher.json`](https://github.com/actions/checkout/blob/main/problem-matcher.json)) in lines 15-19 of [`src/main.ts`](https://github.com/actions/checkout/blob/main/src/main.ts) to convert Git errors into annotations.

The core work happens in `git-source-provider.getSource()` (called from line 21 of [`src/main.ts`](https://github.com/actions/checkout/blob/main/src/main.ts)), which instantiates a `GitCommandManager` and uses `GitAuthHelper.configureAuth()` to set up authentication before running `git init`, `git remote add`, and optionally `git fetch` with sparse-checkout, LFS, or submodule handling. Finally, during the post-run phase (lines 32-47 of [`src/main.ts`](https://github.com/actions/checkout/blob/main/src/main.ts)), `git-source-provider.cleanup()` removes temporary credentials and SSH keys via `GitAuthHelper.removeAuth()`.

## Common Failure Points and Diagnostic Steps

### Authentication Failures (HTTP 401)

When you encounter `Authentication failed` errors, the cause typically involves incorrect **Personal Access Token (PAT)** scopes or missing SSH keys. The action writes authentication headers via `GitAuthHelper` (line 63 of [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts)), logging the `http.<url>.extraheader` entry when debug mode is active.

Enable step debug logging to confirm the token being passed. If using a PAT for private repositories, ensure it has the `repo` scope. For SSH authentication, verify the `ssh-key` input contains a valid private key, and check that `GitAuthHelper.configureSsh()` (lines 250-274) successfully wrote the temporary key file.

### Credential Persistence and Submodule Errors

The error `fatal: could not read Username for 'https://github.com': No such device or address` indicates that `persist-credentials: false` removed the token from `.git/config` before subsequent Git commands executed. This commonly affects submodule operations that require HTTPS authentication.

Check the post-run logs for the message `Removing HTTP extra header` (around line 447 of [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts)). To fix this, either keep `persist-credentials: true` (the default) or explicitly configure submodule authentication by providing an `ssh-key` when using `submodules: true`.

### Shallow Clone Errors

When you see `fatal: remote error: upload-pack: not our ref`, the workflow likely requires commit history that exceeds the shallow clone depth. By default, `fetch-depth` is set to `1`, and `GitCommandManager.fetch()` only passes the `--depth` flag when `fetchDepth > 0` (lines 99-101 of [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts)).

Set `fetch-depth: 0` to fetch complete history, or specify a sufficient depth to reach the commits your workflow needs (e.g., `fetch-depth: 50` for `git checkout HEAD~10`).

### Sparse Checkout Configuration Issues

Sparse checkout requires Git version 2.28 or newer for cone mode. If the runner uses an older version, the action logs `Minimum Git version required for sparse checkout…` (lines 131-134 of [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts)) and skips the operation.

Ensure your runner has Git 2.28+ (GitHub-hosted runners include this), or force non-cone mode by setting `sparse-checkout-cone-mode: false` when using self-hosted runners with legacy Git installations.

### Submodule Authentication Failures

When submodules use SSH URLs (`git@github.com:`) but no `ssh-key` is supplied, checkout fails because `GitAuthHelper.configureSubmoduleAuth()` (lines 155-229) cannot write valid authentication into each submodule's `.git/config`.

Look for the log message `Configuring submodule auth` to confirm the action is processing submodules. Provide a valid `ssh-key` input, or let the action automatically convert SSH URLs to HTTPS by omitting the SSH key (the default behavior when `ssh-key` is absent).

### Git LFS File Retrieval Failures

Missing LFS files indicate either `lfs: true` is not set in your workflow, or the runner's `git-lfs` version is older than 2.1. The action validates versions in `GitCommandManager.initializeCommandManager()` (lines 700-712).

Enable `lfs: true` in your checkout step and verify the runner has a recent `git-lfs` installation. GitHub-hosted runners include compatible versions by default.

### Unsafe PR Checkout Blocks

Workflows triggered by `pull_request_target` or `workflow_run` events block checkout of fork refs unless explicitly permitted. The security guard in [`src/unsafe-pr-checkout-helper.ts`](https://github.com/actions/checkout/blob/main/src/unsafe-pr-checkout-helper.ts) logs warnings when attempting to checkout potentially unsafe code.

Only enable `allow-unsafe-pr-checkout: true` after reviewing GitHub's security guidance, and only when you must checkout the fork's code in a trusted context.

### Safe Directory Warnings on Self-Hosted Runners

Git 2.35.2+ introduced stricter ownership checks that refuse to operate in directories not marked as safe. `GitDirectoryHelper` (lines 96-115) automatically adds the repository path to `safe.directory` when `set-safe-directory: true` (the default).

If you encounter `safe.directory` warnings, ensure you haven't disabled this setting, or manually configure the safe directory in your workflow environment.

## Debugging Techniques

To systematically troubleshoot `actions/checkout` errors, follow these diagnostic steps:

- **Enable step debug logging** – Set the secret `ACTIONS_STEP_DEBUG` to `true` in your repository settings or export the environment variable. This exposes all `core.debug` statements, including resolved inputs and exact Git commands executed by `GitCommandManager.execGit`.

- **Inspect problem matcher output** – The action registers `::add-matcher::` with [`problem-matcher.json`](https://github.com/actions/checkout/blob/main/problem-matcher.json) (lines 15-19 of [`src/main.ts`](https://github.com/actions/checkout/blob/main/src/main.ts)). Errors appear as annotations in the GitHub UI, providing direct links to failure lines.

- **Check post-run cleanup logs** – The cleanup step runs after job completion. If `GitAuthHelper.removeAuth()` fails to remove credentials (lines 333-336), you'll see warnings that could indicate permission issues on the runner.

- **Validate the runner Git version** – The action aborts early if Git is too old. Output `git --version` in a separate step to confirm compatibility with features like sparse-checkout or partial clones.

- **Use verbose Git output** – Add `debug: true` to your checkout step to force verbose Git output, which the action passes directly to underlying `git` commands.

## Practical Configuration Examples

### Enable Full Debug Logging

```yaml

# .github/workflows/debug.yml

name: Debug Checkout
on: push
jobs:
  debug:
    runs-on: ubuntu-latest
    steps:
      - name: Enable step debug
        env:
          ACTIONS_STEP_DEBUG: true
        run: echo "debug enabled"
      - name: Checkout with full logs
        uses: actions/checkout@v7
        with:
          fetch-depth: 0
          submodules: true
          lfs: true

```

### Diagnose Authentication Problems

```yaml
- name: Checkout private repo
  uses: actions/checkout@v7
  with:
    repository: my-org/private-repo
    token: ${{ secrets.PAT }}
    persist-credentials: true

```

The debug logs will show the `http.<url>.extraheader` entry that `GitAuthHelper` writes at line 63 of [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts).

### Sparse Checkout Configuration

```yaml
- name: Checkout only docs
  uses: actions/checkout@v7
  with:
    sparse-checkout: |
      docs/
      README.md

```

For legacy runners with Git < 2.28:

```yaml
- name: Checkout with legacy sparse mode
  uses: actions/checkout@v7
  with:
    sparse-checkout: docs/
    sparse-checkout-cone-mode: false

```

### Submodule Handling with SSH

```yaml
- name: Checkout repo with submodules via SSH
  uses: actions/checkout@v7
  with:
    submodules: recursive
    ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
    ssh-strict: true

```

The action writes the SSH key to a temporary file and sets `GIT_SSH_COMMAND` via `GitAuthHelper.configureSsh()` (lines 250-274).

### Allow Unsafe PR Checkout

```yaml
- name: Checkout PR from fork (pull_request_target)
  uses: actions/checkout@v7
  with:
    ref: ${{ github.event.pull_request.head.sha }}
    allow-unsafe-pr-checkout: true

```

Only use this after reviewing the security implications in [`src/unsafe-pr-checkout-helper.ts`](https://github.com/actions/checkout/blob/main/src/unsafe-pr-checkout-helper.ts).

## Summary

- **Authentication errors** trace to [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts) and require checking token scopes or SSH key configuration via `configureAuth()` and `configureSsh()`.
- **Shallow clone failures** resolve by increasing `fetch-depth` from the default `1` to `0` (full history) or a specific commit count.
- **Submodule errors** require `persist-credentials: true` or explicit `ssh-key` configuration handled by `configureSubmoduleAuth()` (lines 155-229).
- **Sparse checkout** requires Git 2.28+ for cone mode, validated in [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts) (lines 131-134).
- **Debug systematically** using `ACTIONS_STEP_DEBUG` to expose the exact Git commands and inputs parsed by [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts).

## Frequently Asked Questions

### Why does my checkout fail with "Authentication failed" even when using the default GITHUB_TOKEN?

The default `GITHUB_TOKEN` has limited scopes and cannot access private repositories or submodules in other organizations. According to [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts) (line 63), the action writes the token as an HTTP extra header. If the token lacks `repo` scope or the repository requires specific permissions, Git returns HTTP 401. Use a PAT with appropriate scopes or configure `ssh-key` for private repositories.

### How do I fix "fatal: shallow update not allowed" when checking out a pull request?

This error occurs when `fetch-depth` is set to `1` (the default) but the workflow requires older commits. As implemented in [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts) (lines 99-101), the action only adds `--depth` to the fetch command when `fetchDepth > 0`. Set `fetch-depth: 0` to fetch complete history, or specify a depth sufficient to include the commits your workflow needs.

### Why are my Git LFS files missing after checkout?

Git LFS files require explicit enabling via `lfs: true` in your workflow inputs. The action checks the runner's `git-lfs` version in `GitCommandManager.initializeCommandManager()` (lines 700-712) and skips LFS operations if the version is older than 2.1. Ensure your runner has a recent `git-lfs` installation and enable the LFS option in your checkout step.

### How do I troubleshoot submodule authentication errors?

Submodule failures typically occur when submodule URLs use SSH but no `ssh-key` is provided, or when `persist-credentials: false` removes authentication before submodule initialization. The action configures submodule auth via `GitAuthHelper.configureSubmoduleAuth()` (lines 155-229 of [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts)), writing the same auth config into each submodule's `.git/config`. Provide an `ssh-key` for SSH submodules, or keep `persist-credentials: true` for HTTPS authentication to persist through subsequent Git commands.