hashicorp/terraform · error

error parsing GCS URL

Error message

error parsing GCS URL: %s

What it means

Returned by detectGCS when url.Parse rejects the synthesized URL 'https://www.googleapis.com/storage/<version>/<bucket>/<object>'. Because the URL is built from parts that already passed the length check, a parse failure means one of the bucket/object segments contains characters illegal in a URL (control chars, spaces, invalid percent-encoding). The %s is the url.Parse error.

Solutions

  1. URL-encode the bucket and object segments before constructing the source string.
  2. Use the explicit gcs:: prefix with an already-valid https URL.
  3. Rename the GCS object to avoid spaces/special characters and update the source.

Example fix

# before (space in object key -> parse fails)
source = "googleapis.com/storage/v1/bucket/my module.tar.gz"

# after (encoded)
source = "gcs::https://www.googleapis.com/storage/v1/bucket/my%20module.tar.gz"
Defensive patterns

Strategy: validation

Validate before calling

// URL-encode bucket/object before building a GCS URL.
// func buildGCSURL(version, bucket, object string) (string, error) {
//     u := fmt.Sprintf("https://www.googleapis.com/storage/%s/%s/%s",
//         url.PathEscape(version), url.PathEscape(bucket), url.PathEscape(object))
//     _, err := url.Parse(u)
//     return u, err
// }

Prevention

When it happens

Trigger: A GCS object key with raw spaces, control characters, or malformed percent-encoding is concatenated into the URL string and url.Parse fails.

Common situations: Object keys with spaces or special characters pasted verbatim; percent-encoded keys that are already partially encoded; locale-specific characters in bucket names.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/09f0ddde8a7bec94. Report an issue: GitHub.

Appendix: source

Thrown at internal/getmodules/moduleaddrs/detect_gcs.go:32

func detectGCS(src string) (string, bool, error) {
	if len(src) == 0 {
		return "", false, nil
	}

	if strings.Contains(src, "googleapis.com/") {
		parts := strings.Split(src, "/")
		if len(parts) < 5 {
			return "", false, fmt.Errorf(
				"URL is not a valid GCS URL")
		}
		version := parts[2]
		bucket := parts[3]
		object := strings.Join(parts[4:], "/")

		url, err := url.Parse(fmt.Sprintf("https://www.googleapis.com/storage/%s/%s/%s",
			version, bucket, object))
		if err != nil {
			return "", false, fmt.Errorf("error parsing GCS URL: %s", err)
		}

		return "gcs::" + url.String(), true, nil
	}

	return "", false, nil
}

View on GitHub (pinned to d32a084675)