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

  1. Append the bucket name (and key) after the host: s3.amazonaws.com/my-bucket/modules/vpc.
  2. If the bucket is supplied via a variable, validate it is non-empty before rendering the source string.
  3. 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

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


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