hashicorp/packer · error

must contain only letters, digits, and dashes, and may not u

Error message

must contain only letters, digits, and dashes, and may not use leading or trailing dashes: %w

What it means

ParsePluginPart runs idna.Lookup.ToUnicode on the plugin source component. If that fails, the component is not a valid IDNA label: it must contain only letters, digits, and dashes, without leading or trailing dashes. The IDNA library error is wrapped with %w for inspection.

Source

Thrown at hcl2template/addrs/plugin.go:89

	// The idna library doesn't expose individual label parsing directly, but
	// once we've verified it doesn't contain any dots we can just treat it
	// like a top-level domain for this library's purposes.
	if strings.ContainsRune(given, '.') {
		return "", fmt.Errorf("dots are not allowed")
	}

	// We don't allow names containing multiple consecutive dashes, just as
	// a matter of preference: they look confusing, or incorrect.
	// This also, as a side-effect, prevents the use of the "punycode"
	// indicator prefix "xn--" that would cause the IDNA library to interpret
	// the given name as punycode, because that would be weird and unexpected.
	if strings.Contains(given, "--") {
		return "", fmt.Errorf("cannot use multiple consecutive dashes")
	}

	result, err := idna.Lookup.ToUnicode(given)
	if err != nil {
		return "", fmt.Errorf("must contain only letters, digits, and dashes, and may not use leading or trailing dashes: %w", err)
	}

	return result, nil
}

// IsPluginPartNormalized compares a given string to the result of ParsePluginPart(string)
func IsPluginPartNormalized(str string) (bool, error) {
	normalized, err := ParsePluginPart(str)
	if err != nil {
		return false, err
	}
	if str == normalized {
		return true, nil
	}
	return false, nil
}

// ParsePluginSourceString parses the source attribute and returns a plugin.

View on GitHub (pinned to eb36e3c3e4)

Solutions

  1. Replace invalid characters (underscores, dots, symbols) with dashes or remove them
  2. Remove any leading or trailing dashes from the component
  3. Verify the source address against the documented format 'github.com/org/name'

Example fix

// before
source "github.com/hashicorp/packer_plugin_amazon"
// after
source "github.com/hashicorp/packer-plugin-amazon"
Defensive patterns

Strategy: validation

Validate before calling

func validChars(s string) bool {
  for _, r := range s {
    if !(r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || r == '-') { return false }
  }
  return len(s) > 0 && s[0] != '-' && s[len(s)-1] != '-'
}

Type guard

func isIDNASafe(s string) bool { _, err := idna.Lookup.ToUnicode(s); return err == nil }

Prevention

When it happens

Trigger: ParsePluginPart (or ParsePluginSourceString / checkPluginNameNormalized) given a component with characters outside [a-zA-Z0-9-], or one starting/ending with '-', e.g. 'github.com/hashicorp/packer_plugin', '-amazon-', or 'amazon!'.

Common situations: Underscores in plugin names (common mistake, since repos often use them); special characters from copy-paste; trailing dash left from editing; uppercase is fine but symbols are not.

Related errors


AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05). Data as JSON: /api/errors/ea82ec947c8a13fe. Report an issue: GitHub.