{"record":{"id":"b6177ec3779e4056","repo":"hashicorp/terraform","slug":"can-t-use-local-directory-q-as-a-module-registry","errorCode":null,"errorMessage":"can't use local directory %q as a module registry address","messagePattern":"can't use local directory %q as a module registry address","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/getmodules/moduleaddrs/source_parsing.go","lineNumber":156,"sourceCode":"\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// ParseModuleSourceRegistry is a variant of ParseModuleSource which only\n// accepts module registry addresses, and will reject any other address type.\n//\n// Use this instead of ParseModuleSource if you know from some other surrounding\n// context that an address is intended to be a registry address rather than\n// some other address type, which will then allow for better error reporting\n// due to the additional information about user intent.\nfunc ParseModuleSourceRegistry(raw string) (addrs.ModuleSource, error) {\n\t// Before we delegate to the \"real\" function we'll just make sure this\n\t// doesn't look like a local source address, so we can return a better\n\t// error message for that situation.\n\tif isModuleSourceLocal(raw) {\n\t\treturn addrs.ModuleSourceRegistry{}, fmt.Errorf(\"can't use local directory %q as a module registry address\", raw)\n\t}\n\n\tsrc, err := tfaddr.ParseModuleSource(raw)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn addrs.ModuleSourceRegistry{\n\t\tPackage: src.Package,\n\t\tSubdir:  src.Subdir,\n\t}, nil\n}\n\nfunc parseModuleSourceRemote(raw string) (addrs.ModuleSourceRemote, error) {\n\tvar subDir string\n\traw, subDir = SplitPackageSubdir(raw)\n\tif strings.HasPrefix(subDir, \"../\") {\n\t\treturn addrs.ModuleSourceRemote{}, fmt.Errorf(\"subdirectory path %q leads outside of the module package\", subDir)\n\t}","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/getmodules/moduleaddrs/source_parsing.go#L138-L174","documentation":"Raised by ParseModuleSourceRegistry (internal/getmodules/moduleaddrs/source_parsing.go:156). This strict parser accepts ONLY registry addresses, so if the source begins with a local-path prefix (./, ../, .\\, ..\\) it is rejected outright with this message. The caller chose the registry-only parser but supplied a local relative path.","triggerScenarios":"Calling ParseModuleSourceRegistry(\"./modules/foo\") or ParseModuleSourceRegistry(\"../shared/network\"). Any string starting with a recognized local prefix trips this guard before registry parsing is attempted.","commonSituations":"A tool or code path that intentionally restricts inputs to registry addresses receiving a local path; user confusion between the general ParseModuleSource and the registry-only variant; misrouted input in a wrapper CLI.","solutions":["If local paths should be allowed, call ParseModuleSource (the general parser) instead of ParseModuleSourceRegistry.","If only registry addresses are valid for your context, reject or re-route local paths upstream and surface a clearer message to the user.","Ensure the input is a genuine registry address (namespace/name/system or host/namespace/name/system)."],"exampleFix":"// before\naddr, err := moduleaddrs.ParseModuleSourceRegistry(\"./modules/foo\")\n// after\naddr, err := moduleaddrs.ParseModuleSource(\"./modules/foo\")","handlingStrategy":"validation","validationCode":"// Dispatch to the right parser based on whether the source is local.\nfunc parseSource(raw string) (addrs.ModuleSource, error) {\n\tfor _, p := range []string{\"./\", \"../\", \".\\\\\", \"..\\\\\"} {\n\t\tif strings.HasPrefix(raw, p) {\n\t\t\t// Local path: do NOT use the registry-only parser.\n\t\t\treturn moduleaddrs.ParseModuleSource(raw)\n\t\t}\n\t}\n\treturn moduleaddrs.ParseModuleSourceRegistry(raw)\n}","typeGuard":null,"tryCatchPattern":"if _, err := moduleaddrs.ParseModuleSourceRegistry(raw); err != nil {\n    if strings.HasPrefix(raw, \"./\") || strings.HasPrefix(raw, \"../\") {\n        // Wrong parser for a local path; retry with the general parser.\n        return moduleaddrs.ParseModuleSource(raw)\n    }\n    return nil, err\n}","preventionTips":["Use ParseModuleSource unless you specifically require registry-only semantics.","Validate the input shape and route local paths away from the registry parser.","Document which parser each code path expects to avoid misrouting."],"tags":["module-source","registry","validation","parsing"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}