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.
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 scheduledCommon 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.
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.SUBMODULE_PAT }} # PAT with access to the submodule reposInitialize submodules manually
If you clone yourself, initialize and update recursively after the parent clone.
git submodule sync --recursive
git submodule update --init --recursiveNormalize the submodule protocol
Force submodule URLs to the protocol your CI is authenticated for, e.g. rewrite SSH to HTTPS.
git config --global url."https://github.com/".insteadOf "git@github.com:"
git submodule update --init --recursiveHow to prevent it
- Use
submodules: recursivewith a token/key that can read every submodule repo. - Keep
.gitmodulesURLs on a protocol your CI is authenticated for. - Grant deploy keys / App installations access to submodule repos, not just the parent.