docker/compose · error
git subdirectory path traversal detected: %s
Error message
git subdirectory path traversal detected: %s
What it means
The path-traversal guard of `validateGitSubDir`: after cleaning, the sub-directory equals `..` or starts with `../` (or `..\`), meaning it would escape the checked-out repository root. Compose rejects it to prevent includes from reading files outside the git cache directory.
Source
Thrown at pkg/remote/git.go:148
return local, err
}
func (g gitRemoteLoader) Dir(path string) string {
return g.known[path]
}
// validateGitSubDir ensures a subdirectory path is contained within the base directory
// and doesn't escape via path traversal. Unlike validatePathInBase for OCI artifacts,
// this allows nested directories but prevents traversal outside the base.
func validateGitSubDir(base, subDir string) error {
cleanSubDir := filepath.Clean(subDir)
if filepath.IsAbs(cleanSubDir) {
return fmt.Errorf("git subdirectory must be relative, got: %s", subDir)
}
if cleanSubDir == ".." || strings.HasPrefix(cleanSubDir, "../") || strings.HasPrefix(cleanSubDir, "..\\") {
return fmt.Errorf("git subdirectory path traversal detected: %s", subDir)
}
if len(cleanSubDir) >= 2 && cleanSubDir[1] == ':' {
return fmt.Errorf("git subdirectory must be relative, got: %s", subDir)
}
targetPath := filepath.Join(base, cleanSubDir)
cleanBase := filepath.Clean(base)
cleanTarget := filepath.Clean(targetPath)
// Ensure the target starts with the base path
relPath, err := filepath.Rel(cleanBase, cleanTarget)
if err != nil {
return fmt.Errorf("invalid git subdirectory path: %w", err)
}
if relPath == ".." || strings.HasPrefix(relPath, "../") || strings.HasPrefix(relPath, "..\\") {
return fmt.Errorf("git subdirectory escapes base directory: %s", subDir)View on GitHub (pinned to ddc4b044b6)
Solutions
- Point the include at a path inside the repository: `git://host/repo.git#ref:valid/subdir`
- If you need files from another repo, declare a second include for that repo instead of traversing with `..`
Example fix
# before include: - path: git://github.com/org/repo.git#main:../shared # after include: - path: git://github.com/org/repo.git#main:shared - path: git://github.com/org/shared.git#main
Defensive patterns
Strategy: validation
Validate before calling
# reject traversal fragments before invoking compose
python3 - <<'EOF'
import sys, posixpath
for arg in sys.argv[1:]:
if '#' not in arg: continue
sub = arg.split('#',1)[1].split(':',1)[1] if ':' in arg.split('#',1)[1] else ''
c = posixpath.normpath(sub)
if c == '..' or c.startswith('../') or c.startswith('..\\'):
sys.exit(f"traversal in include fragment: {arg}")
EOF Prevention
- Never hand-build include fragments from untrusted input without normalizing
- Reference sibling repos as separate includes instead of `..` paths
When it happens
Trigger: An include fragment crafted (or typo'd) as `..`, `../other-project`, `..\other`, or a path like `subdir/../../..` that survives cleaning with a `..` prefix.
Common situations: Attempting to include a sibling checkout via relative `..` paths; malformed include strings from templating; the error is also the expected response to malicious path-traversal payloads.
Related errors
- git subdirectory escapes base directory: %s
- COMPOSE_EXPERIMENTAL_GIT_REMOTE environment variable expects
- git remote resource is disabled by %q
- initializing remote resource cache: %w
- git subdirectory must be relative, got: %s
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/9613a10d904ebaf1.
Report an issue: GitHub.