hashicorp/terraform · error
subdirectory path %q leads outside of the module package
Error message
subdirectory path %q leads outside of the module package
What it means
Raised in parseModuleSourceRemote (internal/getmodules/moduleaddrs/source_parsing.go:173). The source is split into package + subdir (the part after '//'); if that user-supplied subdir begins with '../', it would traverse above the package root, which is forbidden. Terraform blocks this to prevent path-escape / arbitrary file access.
Source
Thrown at internal/getmodules/moduleaddrs/source_parsing.go:173
if isModuleSourceLocal(raw) {
return addrs.ModuleSourceRegistry{}, fmt.Errorf("can't use local directory %q as a module registry address", raw)
}
src, err := tfaddr.ParseModuleSource(raw)
if err != nil {
return nil, err
}
return addrs.ModuleSourceRegistry{
Package: src.Package,
Subdir: src.Subdir,
}, nil
}
func parseModuleSourceRemote(raw string) (addrs.ModuleSourceRemote, error) {
var subDir string
raw, subDir = SplitPackageSubdir(raw)
if strings.HasPrefix(subDir, "../") {
return addrs.ModuleSourceRemote{}, fmt.Errorf("subdirectory path %q leads outside of the module package", subDir)
}
// A remote source address is really just a go-getter address resulting
// from go-getter's "detect" phase, which adds on the prefix specifying
// which protocol it should use and possibly also adjusts the
// protocol-specific part into different syntax.
//
// Note that for historical reasons this can potentially do network
// requests in order to disambiguate certain address types, although
// that's a legacy thing that is only for some specific, less-commonly-used
// address types. Most just do local string manipulation. We should
// aim to remove the network requests over time, if possible.
norm, moreSubDir, err := NormalizePackageAddress(raw)
if err != nil {
// We must pass through the returned error directly here because
// the getmodules package has some special error types it uses
// for certain cases where the UI layer might want to include a
// more helpful error message.View on GitHub (pinned to c9def3e214)
Solutions
- Remove leading '../' from the subdir so it resolves inside the package root.
- If you need a sibling package, reference it by its own full source address rather than traversing out.
- Use path.Clean and assert the result stays within the package before submitting the source.
Example fix
// before
module "x" { source = "git::https://example.com/repo.git//../other-repo" }
// after
module "x" { source = "git::https://example.com/repo.git//modules/other" } Defensive patterns
Strategy: validation
Validate before calling
// Reject a subdir that escapes the package via parent traversal.
func subdirStaysInPackage(subDir string) bool {
if subDir == "" {
return true
}
cleaned := path.Clean(subDir)
return cleaned != ".." && !strings.HasPrefix(cleaned, "../")
} Try / catch
remoteAddr, err := moduleaddrs.ParseModuleSourceRemote(raw)
if err != nil && strings.Contains(err.Error(), "leads outside of the module package") {
return fmt.Errorf("subdir in %q escapes the package; use a path inside the repo root", raw)
} Prevention
- Never start a subdir with ../ ; keep it within the package root.
- Reference sibling packages by their own full source address.
- Lint module sources for parent-traversal sequences in CI.
When it happens
Trigger: A remote source with an explicit subdir that climbs out of the package, e.g. git::https://example.com/repo.git//../../etc or https://example.com/repo.zip//../sibling. The leading '../' in the cleaned subdir is the trigger.
Common situations: Deliberate or accidental parent traversal in a subdir; attempts to reference a sibling package via relative paths; malicious input in a multi-tenant wrapper.
Related errors
- detected subdirectory path %q of %q leads outside of the mod
- Error parsing URL: %s
- subdir %q not found
- subdir %q matches multiple paths
- acl value invalid, expected %s or %s, got %s
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/b7610ad3326baa30.
Report an issue: GitHub.