Skip to content
Latchkey

GitHub Actions Checkout Submodule Fails - Private Submodule Auth

A checkout with submodules fails on a private or cross-repo submodule because the default GITHUB_TOKEN cannot read it, or the submodule uses an SSH URL the runner has no key for.

What this error means

actions/checkout with submodules: true or recursive fails fetching a submodule with a 403 / authentication error, while the main repo checks out fine.

Actions log
fatal: could not read Username for 'https://github.com': terminal prompts disabled
Error: fetching submodule libfoo failed
# or
git@github.com: Permission denied (publickey).

Common causes

Default token cannot read the submodule repo

The built-in GITHUB_TOKEN is scoped to the current repository. A private submodule in another repo is not readable with it.

Submodule uses an SSH URL

A .gitmodules entry with a git@github.com: URL needs an SSH key on the runner; checkout cannot authenticate it with an HTTPS token.

How to fix it

Pass a token that can read the submodule

Use a PAT or GitHub App token with access to the submodule repositories so checkout can fetch them over HTTPS.

.github/workflows/ci.yml
- uses: actions/checkout@v4
  with:
    submodules: recursive
    token: ${{ secrets.SUBMODULE_PAT }}

Use an SSH key or rewrite the URL

  1. Add an SSH deploy key with webfactory/ssh-agent and set submodules: recursive.
  2. Or convert SSH submodule URLs to HTTPS so the token-based auth applies.
  3. Confirm the .gitmodules paths and URLs are correct and reachable.

How to prevent it

  • Provide a scoped PAT or App token for private submodules.
  • Standardize submodule URLs on HTTPS for token auth in CI.
  • Limit submodule fetch depth where full history is not needed.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →