docker/compose · error

git subdirectory must be relative, got: %s

Error message

git subdirectory must be relative, got: %s

What it means

`validateGitSubDir` guards the sub-directory fragment of a git remote include. This variant fires when the cleaned sub-path is absolute — e.g. starts with `/` on Unix. The loader refuses absolute sub-paths because they cannot be joined onto the checkout root safely.

Source

Thrown at pkg/remote/git.go:144

	}
	if stat.IsDir() {
		local, err = findFile(cli.DefaultFileNames, local)
	}
	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)

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Remove the leading slash: use `git://host/repo.git#main:subdir` not `git://host/repo.git#main:/subdir`
  2. Keep the fragment relative to the repository root

Example fix

# before
include:
  - path: git://github.com/org/repo.git#main:/compose/prod

# after
include:
  - path: git://github.com/org/repo.git#main:compose/prod
Defensive patterns

Strategy: validation

Validate before calling

# keep include fragments relative (no leading slash)
python3 - <<'EOF'
import sys
for arg in sys.argv[1:]:
    frag = arg.split('#', 1)[1] if '#' in arg else ''
    sub = frag.split(':', 1)[1] if ':' in frag else ''
    if sub.startswith(('/', '\\')):
        sys.exit(f"absolute include fragment: {arg}")
EOF "$@"

Prevention

When it happens

Trigger: An include path like `git://host/repo.git#main:/etc/config` where the fragment after the ref parses as an absolute path; also a Windows path like `\server\share` cleaning to an absolute form.

Common situations: Users writing sub-paths with a leading slash out of habit; converting a local `include: /abs/path` to a git include and keeping the leading slash; copy-paste from shell examples with absolute paths.

Related errors


AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15). Data as JSON: /api/errors/b6c3771a9c75e192. Report an issue: GitHub.