hashicorp/terraform · error
URL is not a valid S3 URL
Error message
URL is not a valid S3 URL
What it means
Raised by detectS3 (internal/getmodules/moduleaddrs/detect_s3.go:22). The source contains '.amazonaws.com/' so S3 detection engages, but splitting the string on '/' yields fewer than 2 parts, meaning there is no bucket segment after the host. A valid schemeless S3 reference needs at least a host and a bucket (e.g. s3.amazonaws.com/bucket).
Source
Thrown at internal/getmodules/moduleaddrs/detect_s3.go:22
package moduleaddrs
import (
"fmt"
"net/url"
"strings"
)
// detectS3 detects strings that seem like schemeless references to
// Amazon S3 and translates them into URLs for the "s3" getter.
func detectS3(src string) (string, bool, error) {
if len(src) == 0 {
return "", false, nil
}
if strings.Contains(src, ".amazonaws.com/") {
parts := strings.Split(src, "/")
if len(parts) < 2 {
return "", false, fmt.Errorf(
"URL is not a valid S3 URL")
}
hostParts := strings.Split(parts[0], ".")
if len(hostParts) == 3 {
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
}View on GitHub (pinned to c9def3e214)
Solutions
- Append the bucket name (and key) after the host: s3.amazonaws.com/my-bucket/modules/vpc.
- If the bucket is supplied via a variable, validate it is non-empty before rendering the source string.
- Prefer the explicit s3::https://... URL form so missing segments are more obvious.
Example fix
// before
module "x" { source = "s3.amazonaws.com/${var.bucket}" } // var.bucket is empty
// after
module "x" { source = "s3.amazonaws.com/my-bucket/modules/vpc" } Defensive patterns
Strategy: validation
Validate before calling
// Ensure a schemeless S3 reference has at least a host + bucket.
func validS3Shorthand(src string) bool {
if !strings.Contains(src, ".amazonaws.com/") {
return false
}
parts := strings.Split(src, "/")
return len(parts) >= 2 && parts[1] != ""
} Try / catch
addr, err := moduleaddrs.ParseModuleSource(src)
if err != nil && strings.Contains(src, "amazonaws.com") {
return fmt.Errorf("S3 source %q is missing a bucket/key segment: %w", src, err)
} Prevention
- Always include the bucket name and key after the S3 host.
- Validate that templated bucket variables are non-empty before rendering the source.
- Prefer the explicit s3::https:// form for clarity.
When it happens
Trigger: A source like 's3.amazonaws.com/' (trailing slash, nothing after) or just a hostname with no bucket, e.g. 's3.us-east-1.amazonaws.com'. The detector entered because '.amazonaws.com/' was present but found no bucket path component.
Common situations: Truncated or templated S3 URL where the bucket variable rendered empty; a copy-paste that lost the bucket/key portion; a CI pipeline injecting an empty bucket name.
Related errors
- error parsing S3 URL: %s
- missing state name
- can't delete default state
- lock ID '%s' does not match the existing lock ID '%s'
- URL is not a valid GCS URL
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/0ede26d4c4fadd7d.
Report an issue: GitHub.