{"record":{"id":"b9bb3444d3d32d1f","repo":"hashicorp/terraform","slug":"detected-subdirectory-path-q-of-q-leads-outside","errorCode":null,"errorMessage":"detected subdirectory path %q of %q leads outside of the module package","messagePattern":"detected subdirectory path %q of %q leads outside of the module package","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/getmodules/moduleaddrs/source_parsing.go","lineNumber":209,"sourceCode":"\t\t// more helpful error message.\n\t\treturn addrs.ModuleSourceRemote{}, err\n\t}\n\n\tif moreSubDir != \"\" {\n\t\tswitch {\n\t\tcase subDir != \"\":\n\t\t\t// The detector's own subdir goes first, because the\n\t\t\t// subdir we were given is conceptually relative to\n\t\t\t// the subdirectory that we just detected.\n\t\t\tsubDir = path.Join(moreSubDir, subDir)\n\t\tdefault:\n\t\t\tsubDir = path.Clean(moreSubDir)\n\t\t}\n\t\tif strings.HasPrefix(subDir, \"../\") {\n\t\t\t// This would suggest a bug in a go-getter detector, but\n\t\t\t// we'll catch it anyway to avoid doing something confusing\n\t\t\t// downstream.\n\t\t\treturn addrs.ModuleSourceRemote{}, fmt.Errorf(\"detected subdirectory path %q of %q leads outside of the module package\", subDir, norm)\n\t\t}\n\t}\n\n\treturn addrs.ModuleSourceRemote{\n\t\tPackage: addrs.ModulePackage(norm),\n\t\tSubdir:  subDir,\n\t}, nil\n}\n","sourceCodeStart":191,"sourceCodeEnd":218,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/getmodules/moduleaddrs/source_parsing.go#L191-L218","documentation":"Raised in parseModuleSourceRemote (internal/getmodules/moduleaddrs/source_parsing.go:209). This is the detector-driven counterpart of the previous guard: after NormalizePackageAddress returns an additional subdir (moreSubDir), it is combined with any user subdir and cleaned; if the result still begins with '../', the combined path would escape the package. The code comment notes this would suggest a bug in a go-getter detector, but Terraform catches it defensively.","triggerScenarios":"A shorthand/remote detector contributes its own subdir that, when joined with the user's subdir and cleaned, yields a '../' prefix. This is rare and usually indicates either a malformed combined address or a detector returning an escaping path.","commonSituations":"Combining a shorthand that carries a subdir (e.g. github.com/user/repo//sub) with an additional explicit '//../up' subdir; obscure detector bugs; non-standard source forms.","solutions":["Avoid stacking a shorthand-embedded subdir together with an additional explicit subdir; use one or the other.","Switch to a single canonical full URL so the detector does not inject its own subdir.","If the source looks valid and minimal, report it as a bug against the relevant detector."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"remoteAddr, err := moduleaddrs.ParseModuleSourceRemote(raw)\nif err != nil && strings.Contains(err.Error(), \"leads outside of the module package\") {\n    // Detector contributed an escaping subdir; retry with a single canonical URL.\n    remoteAddr, err = moduleaddrs.ParseModuleSourceRemote(canonicalURL)\n}","preventionTips":["Do not combine a shorthand that embeds a subdir with an additional explicit subdir.","Use one canonical full URL so detectors do not inject their own subdir.","Report reproducible cases as a detector bug."],"tags":["module-source","path-traversal","subdir","internal"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}