hashicorp/terraform · error

error parsing S3 URL

Error message

error parsing S3 URL: %s

What it means

Returned by detectS3PathStyle (3-label host case) when url.Parse rejects 'https://<region>.amazonaws.com/<parts...>'. Because the region and parts came from the input, a parse failure means invalid characters in the region or key path. The %s is the url.Parse error.

Solutions

  1. URL-encode the object key segments.
  2. Use the explicit s3::https:// form with a verified URL.
  3. Rename the S3 object to remove problematic characters.

Example fix

# before (space in key)
source = "us-east-1.amazonaws.com/bucket/my module.zip"

# after
source = "s3::https://us-east-1.amazonaws.com/bucket/my%20module.zip"
Defensive patterns

Strategy: validation

Validate before calling

// URL-encode the S3 key before constructing a path-style URL.
// enc := url.PathEscape(key)
// u := fmt.Sprintf("https://%s.amazonaws.com/%s", region, enc)
// if _, err := url.Parse(u); err != nil { return err }

Prevention

When it happens

Trigger: A path-style S3 source whose region or object key contains control characters, spaces, or bad percent-encoding causes url.Parse to fail.

Common situations: Spaces or special characters in the object key pasted verbatim; non-ASCII region/bucket characters.

Related errors


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

Appendix: 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 d32a084675)