hashicorp/terraform · error

error parsing S3 URL: %s

Error message

error parsing S3 URL: %s

What it means

Raised in detectS3PathStyle (internal/getmodules/moduleaddrs/detect_s3.go:46). After recognizing a 3-label path-style host (region.amazonaws.com), the code builds 'https://<region>.amazonaws.com/<parts...>' and calls url.Parse on it. If parsing fails (e.g. illegal characters in the bucket/key that survive string concatenation but break the URL grammar), this error wraps the underlying parse error.

Source

Thrown at internal/getmodules/moduleaddrs/detect_s3.go:46

			return detectS3PathStyle(hostParts[0], parts[1:])
		} else if len(hostParts) == 4 {
			return detectS3OldVhostStyle(hostParts[1], hostParts[0], parts[1:])
		} else if len(hostParts) == 5 && hostParts[1] == "s3" {
			return detectS3NewVhostStyle(hostParts[2], hostParts[0], parts[1:])
		} else {
			return "", false, fmt.Errorf(
				"URL is not a valid S3 URL")
		}
	}

	return "", false, nil
}

func detectS3PathStyle(region string, parts []string) (string, bool, error) {
	urlStr := fmt.Sprintf("https://%s.amazonaws.com/%s", region, strings.Join(parts, "/"))
	url, err := url.Parse(urlStr)
	if err != nil {
		return "", false, fmt.Errorf("error parsing S3 URL: %s", err)
	}

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

func detectS3OldVhostStyle(region, bucket string, parts []string) (string, bool, error) {
	urlStr := fmt.Sprintf("https://%s.amazonaws.com/%s/%s", region, bucket, strings.Join(parts, "/"))
	url, err := url.Parse(urlStr)
	if err != nil {
		return "", false, fmt.Errorf("error parsing S3 URL: %s", err)
	}

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

func detectS3NewVhostStyle(region, bucket string, parts []string) (string, bool, error) {
	urlStr := fmt.Sprintf("https://s3.%s.amazonaws.com/%s/%s", region, bucket, strings.Join(parts, "/"))
	url, err := url.Parse(urlStr)

View on GitHub (pinned to c9def3e214)

Solutions

  1. URL-encode bucket and key segments before composing the source string.
  2. Avoid spaces and control characters in S3 object keys used as module sources.
  3. Switch to the explicit s3::https://... form with properly encoded path components.

Example fix

// before
module "x" { source = "s3.amazonaws.com/bucket/my module" }
// after
module "x" { source = "s3.amazonaws.com/bucket/my%20module" }
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-encode path-style S3 bucket/key segments so the reconstructed URL parses.
func encodeS3PathSegments(src string) string {
	if !strings.Contains(src, ".amazonaws.com/") {
		return src
	}
	parts := strings.SplitN(src, "/", 2)
	if len(parts) != 2 {
		return src
	}
	enc := make([]string, 0, len(strings.Split(parts[1], "/")))
	for _, seg := range strings.Split(parts[1], "/") {
		enc = append(enc, url.PathEscape(seg))
	}
	return parts[0] + "/" + strings.Join(enc, "/")
}

Try / catch

if _, err := url.Parse("https://" + reconstructed); err != nil {
    return fmt.Errorf("reconstructed S3 URL is invalid; encode bucket/key segments: %w", err)
}

Prevention

When it happens

Trigger: A bucket or key name containing characters url.Parse rejects (spaces, control characters, or malformed escapes) in a path-style S3 reference such as 's3.amazonaws.com/bucket/my key'.

Common situations: S3 object keys with spaces or non-ASCII characters that were not URL-encoded; templated keys that inject raw special characters.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/38253dbd302f6a86. Report an issue: GitHub.