How to Troubleshoot Actions/Checkout Errors: A Complete Guide to the GitHub Action Source Code
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, 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 to authentication handling in 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. 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). Next, the action registers a problem matcher (problem-matcher.json) in lines 15-19 of 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), 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), 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), 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). 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).
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) 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 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_DEBUGtotruein your repository settings or export the environment variable. This exposes allcore.debugstatements, including resolved inputs and exact Git commands executed byGitCommandManager.execGit. -
Inspect problem matcher output – The action registers
::add-matcher::withproblem-matcher.json(lines 15-19 ofsrc/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 --versionin a separate step to confirm compatibility with features like sparse-checkout or partial clones. -
Use verbose Git output – Add
debug: trueto your checkout step to force verbose Git output, which the action passes directly to underlyinggitcommands.
Practical Configuration Examples
Enable Full Debug Logging
# .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
- 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.
Sparse Checkout Configuration
- name: Checkout only docs
uses: actions/checkout@v7
with:
sparse-checkout: |
docs/
README.md
For legacy runners with Git < 2.28:
- name: Checkout with legacy sparse mode
uses: actions/checkout@v7
with:
sparse-checkout: docs/
sparse-checkout-cone-mode: false
Submodule Handling with SSH
- 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
- 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.
Summary
- Authentication errors trace to
src/git-auth-helper.tsand require checking token scopes or SSH key configuration viaconfigureAuth()andconfigureSsh(). - Shallow clone failures resolve by increasing
fetch-depthfrom the default1to0(full history) or a specific commit count. - Submodule errors require
persist-credentials: trueor explicitssh-keyconfiguration handled byconfigureSubmoduleAuth()(lines 155-229). - Sparse checkout requires Git 2.28+ for cone mode, validated in
src/git-command-manager.ts(lines 131-134). - Debug systematically using
ACTIONS_STEP_DEBUGto expose the exact Git commands and inputs parsed bysrc/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 (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 (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), 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →