hashicorp/terraform · error

must be two words separated by an underscore

Error message

must be two words separated by an underscore

What it means

Returned by ParsePlatform when the input string does not split into exactly two underscore-separated parts. ParsePlatform expects the canonical GOOS_GOARCH form (e.g. 'linux_amd64'); zero underscores or more than one underscore both trip this guard before any whitespace checks run.

Solutions

  1. Use the canonical GOOS_GOARCH form with exactly one underscore, e.g. 'linux_amd64', 'darwin_arm64', 'windows_amd64'.
  2. Confirm the value is not empty and contains no hyphens or extra underscores.
  3. If the value comes from a build flag or env var, validate it against runtime.GOOS/GOARCH known pairs.
  4. Strip any suffix (e.g. '_v1', '_v2') before calling ParsePlatform.

Example fix

// before
platform, err := getproviders.ParsePlatform("linux-amd64")
// after
platform, err := getproviders.ParsePlatform("linux_amd64")
Defensive patterns

Strategy: validation

Validate before calling

// Validate platform string shape before calling ParsePlatform.
func validPlatformShape(s string) bool {
    parts := strings.Split(s, "_")
    return len(parts) == 2 && parts[0] != "" && parts[1] != ""
}

Type guard

// Type-ish guard via regex.
var platformRe = regexp.MustCompile(`^[a-z0-9]+_[a-z0-9]+$`)
func isValidPlatform(s string) bool { return platformRe.MatchString(s) }

Prevention

When it happens

Trigger: A platform string passed to ParsePlatform has 0 underscores ('linuxamd64', 'linux', '') or 2+ underscores ('linux_amd64_arm64', 'a_b_c'). Common entry points: provider target_platform config, CLI flags, decoded registry keys.

Common situations: Typing 'linux-amd64' (hyphen instead of underscore); passing an empty string; passing a triple like 'linux_amd64_v1'; misconfigured provider installation target via TF_REGISTRY_* env or config.

Related errors


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

Appendix: source

Thrown at internal/getproviders/types.go:118

// The ordering is lexical first by OS and then by Architecture.
// This ordering is primarily just to ensure that results of
// functions in this package will be deterministic. The ordering is not
// intended to have any semantic meaning and is subject to change in future.
func (p Platform) LessThan(other Platform) bool {
	switch {
	case p.OS != other.OS:
		return p.OS < other.OS
	default:
		return p.Arch < other.Arch
	}
}

// ParsePlatform parses a string representation of a platform, like
// "linux_amd64", or returns an error if the string is not valid.
func ParsePlatform(str string) (Platform, error) {
	parts := strings.Split(str, "_")
	if len(parts) != 2 {
		return Platform{}, fmt.Errorf("must be two words separated by an underscore")
	}

	os, arch := parts[0], parts[1]
	if strings.ContainsAny(os, " \t\n\r") {
		return Platform{}, fmt.Errorf("OS portion must not contain whitespace")
	}
	if strings.ContainsAny(arch, " \t\n\r") {
		return Platform{}, fmt.Errorf("architecture portion must not contain whitespace")
	}

	return Platform{
		OS:   os,
		Arch: arch,
	}, nil
}

// CurrentPlatform is the platform where the current program is running.
//

View on GitHub (pinned to d32a084675)