hashicorp/terraform · error
subdirectory path leads outside of the module package
Error message
subdirectory path %q leads outside of the module package
What it means
Returned by parseModuleSourceRemote after SplitPackageSubdir extracts a subdir that begins with '../'. Such a subdir would resolve outside the downloaded module package, so it is rejected as a safety/validity guard. The %q is the offending subdir.
Solutions
- Use a subdir that stays within the package, e.g. '//modules/vpc'.
- If you need a sibling package, declare it as a separate module source rather than traversing out.
- Re-examine the part after '//' and remove any leading '../'.
Example fix
# before (escapes package) source = "github.com/org/repo//../other-module" # after (within package) source = "github.com/org/repo//modules/vpc"
Defensive patterns
Strategy: validation
Validate before calling
// Reject subdirs that escape the package before parsing.
// _, sub := SplitPackageSubdir(raw)
// if strings.HasPrefix(sub, "../") {
// return fmt.Errorf("subdir %q escapes the module package", sub)
// } Prevention
- Keep subdirs within the package root (no leading '../').
- Use separate module declarations for sibling packages.
- Lint '//subdir' components in CI.
When it happens
Trigger: A module source like 'github.com/org/repo//../../escape' or any '...//../...' whose subdir portion starts with '../' reaches the remote parser.
Common situations: User tries to point a module at a sibling directory via parent traversal in a remote source; copy-paste of a relative path that worked locally but is meaningless for a remote package; attempted path-traversal.
Related errors
- detected subdirectory path
- Error parsing URL
- can't use local directory
- cannot set mode for credentials file
- downloaded archive does not match the release checksum
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/b7610ad3326baa30.
Report an issue: GitHub.
Appendix: 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 d32a084675)