Skip to content
Latchkey

Git "failed to clone ... a submodule" in CI

The main repo cloned fine, but pulling in a submodule failed - usually because the runner lacks access to the submodule’s remote, the submodule URL is unreachable, or actions/checkout was never told to fetch submodules.

What this error means

The parent clone succeeds, then git submodule update --init (or submodules: true in checkout) fails with fatal: clone of '...' into submodule path '...' failed. Files in the submodule directory are missing or empty.

git submodule output
Cloning into '/runner/_work/app/app/vendor/lib'...
git@github.com: Permission denied (publickey).
fatal: clone of 'git@github.com:org/private-lib.git' into submodule path
'vendor/lib' failed
Failed to clone 'vendor/lib'. Retry scheduled

Common causes

No access to a private submodule

The submodule lives in a separate private repo. The token or SSH key that cloned the parent has no rights to the submodule, so its clone is refused.

Submodules were not requested or initialized

Without submodules: true (Actions) or git submodule update --init --recursive, the submodule directories stay empty - the parent checkout never descended into them.

Submodule URL is SSH but CI only has HTTPS auth (or vice versa)

.gitmodules may use an git@/SSH URL while the runner is authenticated for HTTPS only. The protocol mismatch blocks the submodule clone.

How to fix it

Request submodules with credentials that can read them

On GitHub Actions, fetch submodules and pass a token (or SSH key) with access to the submodule repos.

.github/workflows/ci.yml
- uses: actions/checkout@v4
  with:
    submodules: recursive
    token: ${{ secrets.SUBMODULE_PAT }}   # PAT with access to the submodule repos

Initialize submodules manually

If you clone yourself, initialize and update recursively after the parent clone.

Terminal
git submodule sync --recursive
git submodule update --init --recursive

Normalize the submodule protocol

Force submodule URLs to the protocol your CI is authenticated for, e.g. rewrite SSH to HTTPS.

Terminal
git config --global url."https://github.com/".insteadOf "git@github.com:"
git submodule update --init --recursive

How to prevent it

  • Use submodules: recursive with a token/key that can read every submodule repo.
  • Keep .gitmodules URLs on a protocol your CI is authenticated for.
  • Grant deploy keys / App installations access to submodule repos, not just the parent.

Related guides

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