Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for GitLab CI/CD By James Joyner IV · · 9 min read Last reviewed Jul 2026

GitLab CI Error Guide: 'No url found for submodule path' — Fix Submodule Cloning

Quick answer

Fix 'fatal: No url found for submodule path in .gitmodules' in GitLab CI: set GIT_SUBMODULE_STRATEGY, sync .gitmodules URLs, use relative paths or CI_JOB_TOKEN, and authenticate private submodules correctly.

  • #gitlab
  • #ci-cd
  • #troubleshooting
  • #errors
Free toolkit

Stuck on this GitLab CI/CD error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Overview

GitLab Runner clones your repository at the start of every job. When your project uses Git submodules and the runner tries to initialize them, it fails if the path recorded in the tree has no matching URL entry in .gitmodules:

fatal: No url found for submodule path 'libs/shared' in .gitmodules
ERROR: Job failed: exit status 128

You will also see the closely related variant when the submodule is registered but never checked out, because GIT_SUBMODULE_STRATEGY was left at its default:

Skipping Git submodules setup

Both point at the same root problem: GitLab’s submodule handling is driven by the .gitmodules file and the GIT_SUBMODULE_STRATEGY variable, and one of them is out of sync with what is committed in the tree.

Symptoms

  • The job fails during the “Getting source from Git repository” phase with exit status 128, before any of your script: runs.
  • fatal: No url found for submodule path '<path>' in .gitmodules names a directory that exists as a gitlink in the tree.
  • Submodule directories are empty at build time and your build fails with “file not found” for code that lives in a submodule.
  • The error appears only in CI, while local git clone --recurse-submodules works because your local .git/config still holds a URL that was never committed.
  • After moving or renaming a submodule, the path in the tree and the entry in .gitmodules disagree.

Common Root Causes

  • .gitmodules missing or stale — a submodule was added, moved, or removed but .gitmodules was not committed with a matching [submodule "<path>"] / url = entry.
  • GIT_SUBMODULE_STRATEGY unset — the default is none, so submodules are never fetched; a build then trips over empty directories (the “Skipping Git submodules setup” case).
  • Absolute submodule URLs to a private host.gitmodules uses https://gitlab.example.com/group/dep.git, and the runner has no credentials for it, so the fetch is rejected.
  • Path/URL mismatch after a rename — the gitlink path in the tree was changed but the [submodule "..."] stanza key or path = value was not updated.
  • Submodule on a different host or an SSH URL — CI cannot use your local SSH keys, so an git@-style URL cannot authenticate.
  • Nested submodules without GIT_SUBMODULE_STRATEGY: recursive, so second-level submodules are missed.

Diagnostic Workflow

First confirm what the tree records versus what .gitmodules declares. A gitlink with no .gitmodules entry is the exact cause of the error:

git ls-files --stage | grep ^160000   # 160000 = gitlink (submodule) entries
git config -f .gitmodules --list       # every path=/url= actually committed

Every path printed by the first command must have a matching submodule.<name>.path and submodule.<name>.url in the second. If a path is missing, that is your failure.

Reproduce the runner’s clone locally with the same strategy CI uses:

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

Set the strategy explicitly in .gitlab-ci.yml and prefer relative URLs so submodules resolve against the same GitLab instance and inherit the job token:

variables:
  GIT_SUBMODULE_STRATEGY: recursive
  GIT_SUBMODULE_FORCE_HTTPS: "true"   # rewrite git@/ssh submodule URLs to HTTPS + CI_JOB_TOKEN

build:
  stage: build
  script:
    - ls -la libs/shared          # verify the submodule is populated
    - make build

Always validate the pipeline configuration itself with the CI Lint tool (Project → Build → Pipeline editor → Validate, or /-/ci/lint) before pushing, so a YAML mistake is not masking the real submodule error.

If the submodule lives in a private project, ensure the URL is relative in .gitmodules so CI_JOB_TOKEN can authenticate it:

# .gitmodules — relative path resolves against the parent repo's host
git config -f .gitmodules submodule.libs/shared.url ../../group/shared.git
git add .gitmodules
git commit -m "Use relative submodule URL for CI"

Example Root Cause Analysis

A team renamed a submodule directory from vendor/auth to libs/auth using git mv. Locally everything worked because their .git/config was already initialized. In CI, every pipeline failed with:

fatal: No url found for submodule path 'libs/auth' in .gitmodules
ERROR: Job failed: exit status 128

Running git ls-files --stage | grep ^160000 showed a gitlink at libs/auth, but git config -f .gitmodules --list still listed submodule.vendor/auth.path=vendor/auth. The git mv moved the gitlink in the tree but did not rewrite .gitmodules, so the runner found a submodule path with no declared URL.

The fix was to correct the stanza and re-sync:

git config -f .gitmodules --rename-section submodule.vendor/auth submodule.libs/auth
git config -f .gitmodules submodule.libs/auth.path libs/auth
git submodule sync --recursive
git add .gitmodules
git commit -m "Fix .gitmodules path after submodule rename"

With GIT_SUBMODULE_STRATEGY: recursive already set, the next pipeline cloned the submodule cleanly and the build passed.

Prevention Best Practices

  • Always commit .gitmodules in the same change that adds, moves, or removes a submodule; use git mv plus a manual .gitmodules edit and git submodule sync.
  • Set GIT_SUBMODULE_STRATEGY: recursive explicitly in .gitlab-ci.yml rather than relying on the none default, and use GIT_SUBMODULE_DEPTH to speed up large trees.
  • Prefer relative submodule URLs (../../group/dep.git) so they resolve against the same GitLab instance and authenticate with CI_JOB_TOKEN automatically.
  • Enable GIT_SUBMODULE_FORCE_HTTPS: "true" so git@/SSH submodule URLs are rewritten to HTTPS in CI, where SSH keys are not available.
  • For private cross-project submodules, verify the target project allows the parent project via the CI/CD job token allowlist.
  • Validate every pipeline change with CI Lint before pushing, and add a smoke job that runs ls on submodule paths so an empty submodule fails fast with a clear message.

Quick Command Reference

# List submodule gitlinks recorded in the tree
git ls-files --stage | grep ^160000

# List what .gitmodules actually declares
git config -f .gitmodules --list

# Re-sync configured URLs and re-init submodules
git submodule sync --recursive
git submodule update --init --recursive

# Rewrite a stale stanza after a rename
git config -f .gitmodules --rename-section submodule.OLD submodule.NEW
git config -f .gitmodules submodule.NEW.path NEW/path

# Set a relative URL that works with CI_JOB_TOKEN
git config -f .gitmodules submodule.NEW.url ../../group/dep.git
# .gitlab-ci.yml — reliable submodule handling
variables:
  GIT_SUBMODULE_STRATEGY: recursive
  GIT_SUBMODULE_FORCE_HTTPS: "true"
  GIT_SUBMODULE_DEPTH: 1

Conclusion

No url found for submodule path almost always means the tree contains a submodule gitlink that .gitmodules does not describe — usually after an add, move, or rename where .gitmodules was left behind. Confirm the mismatch with git ls-files --stage | grep ^160000 against git config -f .gitmodules --list, correct the stanza, run git submodule sync, and set GIT_SUBMODULE_STRATEGY: recursive with relative HTTPS URLs so CI clones and authenticates submodules the same way every time.

Free download · 368-page PDF

Fixed it? Get 500 GitLab CI/CD & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.