{"record":{"id":"c7fae97c26492be3","repo":"hashicorp/terraform","slug":"url-is-not-a-valid-gcs-url","errorCode":null,"errorMessage":"URL is not a valid GCS URL","messagePattern":"URL is not a valid GCS URL","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/getmodules/moduleaddrs/detect_gcs.go","lineNumber":22,"sourceCode":"package moduleaddrs\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\t\"strings\"\n)\n\n// detectGCS detects strings that seem like schemeless references to\n// Google Cloud Storage and translates them into URLs for the \"gcs\" getter.\nfunc detectGCS(src string) (string, bool, error) {\n\tif len(src) == 0 {\n\t\treturn \"\", false, nil\n\t}\n\n\tif strings.Contains(src, \"googleapis.com/\") {\n\t\tparts := strings.Split(src, \"/\")\n\t\tif len(parts) < 5 {\n\t\t\treturn \"\", false, fmt.Errorf(\n\t\t\t\t\"URL is not a valid GCS URL\")\n\t\t}\n\t\tversion := parts[2]\n\t\tbucket := parts[3]\n\t\tobject := strings.Join(parts[4:], \"/\")\n\n\t\turl, err := url.Parse(fmt.Sprintf(\"https://www.googleapis.com/storage/%s/%s/%s\",\n\t\t\tversion, bucket, object))\n\t\tif err != nil {\n\t\t\treturn \"\", false, fmt.Errorf(\"error parsing GCS URL: %s\", err)\n\t\t}\n\n\t\treturn \"gcs::\" + url.String(), true, nil\n\t}\n\n\treturn \"\", false, nil\n}\n","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/getmodules/moduleaddrs/detect_gcs.go#L4-L40","documentation":"Returned by detectGCS (detect_gcs.go:22) when the source string contains 'googleapis.com/' (so it looks like a GCS reference) but has fewer than 5 path segments after splitting on '/'. The detector expects the form googleapis.com/<version>/<bucket>/<object...> (at least version+bucket+object), so too few segments means the URL is structurally incomplete.","triggerScenarios":"A module source string referencing a GCS path via googleapis.com but missing required path components. detectGCS splits on '/', requires len(parts) >= 5 (host + version + bucket + at least one object segment), and otherwise returns this error.","commonSituations":"Writing source = \"googleapis.com/storage/v1/my-bucket\" (missing the object/path segment) or \"googleapis.com/storage/v1\" (missing bucket and object). Copy-paste of a partial GCS URL. Misremembering the expected GCS path shape.","solutions":["Provide the full GCS path: googleapis.com/<api-version>/<bucket>/<object-or-prefix>, e.g. googleapis.com/storage/v1/my-bucket/modules/vpc.zip.","If you intended a different source, switch to the explicit gcs:: URL form: gcs::https://www.googleapis.com/storage/v1/bucket/object.","Verify the URL has at least four slashes' worth of segments (host/version/bucket/object).","Use terraform init -from-module with a known-good GCS URL to confirm the format."],"exampleFix":"# before\nsource = \"googleapis.com/storage/v1/my-bucket\"\n\n# after — include the object/prefix segment\nsource = \"googleapis.com/storage/v1/my-bucket/modules/vpc.zip\"","handlingStrategy":"validation","validationCode":"// Validate a GCS shorthand has all required segments before detection\nfunc validGCSShorthand(src string) bool {\n    if !strings.Contains(src, \"googleapis.com/\") {\n        return true // not a GCS shorthand\n    }\n    return len(strings.Split(src, \"/\")) >= 5\n}","typeGuard":"func isCompleteGCSURL(src string) bool {\n    if !strings.Contains(src, \"googleapis.com/\") {\n        return false\n    }\n    return len(strings.Split(src, \"/\")) >= 5\n}","tryCatchPattern":null,"preventionTips":["Always include version, bucket, and object segments in GCS shorthand.","Prefer the explicit gcs::https://... source form to avoid detector ambiguity.","Validate module source URLs in CI before terraform init."],"tags":["getmodules","gcs","module-source","url-parsing","modules"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}