Skip to content
LatchkeyLatchkey home

Private submodule authentication (token or ssh-key) in CI

A private submodule needs its own credential. actions/checkout can fetch submodules only if the token (for HTTPS) or ssh-key (for SSH) it holds can read that submodule repository. The default GITHUB_TOKEN cannot read a different private repo.

What this error means

Submodule update fails with an auth error ("could not read Username" for HTTPS, or "Permission denied (publickey)" for SSH) even though the parent repo cloned successfully.

git
Submodule 'vendor/shared' (https://github.com/acme/shared.git) registered for path 'vendor/shared'
fatal: could not read Username for 'https://github.com': terminal prompts disabled
fatal: clone of 'https://github.com/acme/shared.git' into submodule path 'vendor/shared' failed

Diagnose it: depth, refs, or credentials?

Terminal
git rev-parse --is-shallow-repository
git rev-parse --abbrev-ref HEAD    # prints HEAD when detached
git log --oneline -3
git remote -v

Common causes

The default token is scoped to the parent repo only

GITHUB_TOKEN authenticates the current repository. A submodule in a different private repo is outside its scope, so the fetch is unauthenticated.

The submodule URL style does not match the credential

An HTTPS submodule needs a token; an SSH submodule needs a key. Providing the wrong one leaves the fetch unauthenticated.

How to fix it

Give checkout a credential that covers the submodule (HTTPS token)

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

Or use a deploy key and url rewrite for SSH submodules

  1. Provide the key via ssh-key, or rewrite SSH URLs to token HTTPS.
  2. Keep submodules: recursive so nested submodules resolve.
  3. Grant the credential read-only access to the submodule repo.
Terminal
git config --global url."https://x-access-token:${{ secrets.SUBMODULE_READ_PAT }}@github.com/".insteadOf "https://github.com/"
git submodule update --init --recursive

How to prevent it

  • Provision a token or deploy key that can read every private submodule.
  • Keep all submodule URLs one style so a single credential covers them.
  • Use submodules: recursive to fetch nested submodules in one pass.

Frequently asked questions

What causes Private submodule authentication (token or ssh-key) in CI?
There are 2 common causes: the default token is scoped to the parent repo only and the submodule url style does not match the credential. GITHUB_TOKEN authenticates the current repository.
How do I fix Private submodule authentication (token or ssh-key) in CI?
There are 2 fixes depending on which cause you have: give checkout a credential that covers the submodule (https token) and or use a deploy key and url rewrite for ssh submodules. Work through them in order, since the first is the most common.
What does Private submodule authentication (token or ssh-key) in CI actually mean?
Submodule update fails with an auth error ("could not read Username" for HTTPS, or "Permission denied (publickey)" for SSH) even though the parent repo cloned successfully.
How do I stop Private submodule authentication (token or ssh-key) in CI happening again?
Provision a token or deploy key that can read every private submodule. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card