{"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/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/getmodules/moduleaddrs/source_parsing.go#L191-L218","documentation":"Returned by parseModuleSourceRemote after go-getter detection: the detector itself produced a subdir (moreSubDir) which, when combined with the user-supplied subdir, starts with '../'. The code notes this would indicate a bug in a go-getter detector; it is caught defensively to prevent confusing downstream behavior. The %q values are the combined subdir and the normalized source.","triggerScenarios":"A go-getter detector emits a subdir that, joined with the caller's subdir, escapes the package root — e.g. detector returns '..' or the join places a '../' prefix at the start.","commonSituations":"Bug in a custom or outdated go-getter detector; edge-case source format that triggers an unusual detector subdir; combined subdir logic producing an unexpected prefix.","solutions":["Drop the user-supplied subdir and let the detector handle it alone, or vice-versa.","Upgrade go-getter / Terraform to a version with the detector bug fixed.","Report the source string to the go-getter maintainers with the normalized form from the error.","Switch to an explicit scheme prefix to bypass the buggy detector."],"exampleFix":"# before (triggers detector subdir escape)\nsource = \"github.com/org/repo?ref=v1//../x\"\n\n# after (no parent traversal)\nsource = \"github.com/org/repo//modules/x?ref=v1\"","handlingStrategy":"validation","validationCode":"// After detector normalization, re-check the combined subdir.\n// if strings.HasPrefix(subDir, \"../\") {\n//     return fmt.Errorf(\"detected subdir escapes package; drop user subdir or upgrade go-getter\")\n// }","typeGuard":null,"tryCatchPattern":"// Catch detector-induced escapes and retry without the user subdir.\n// src, err := parseModuleSourceRemote(raw)\n// if err != nil && strings.Contains(err.Error(), \"leads outside of the module package\") {\n//     raw2 := raw[:strings.Index(raw, \"//\")] // strip user subdir\n//     src, err = parseModuleSourceRemote(raw2)\n// }","preventionTips":["Keep go-getter and Terraform up to date to pick up detector fixes.","Avoid combining a user subdir with sources whose detectors emit their own subdir.","Report detector escape bugs upstream with the normalized source from the error."],"tags":["module-address","subdir","go-getter","path-traversal"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}