hashicorp/terraform · error
: name may contain only lowercase letters, digits and…
Error message
%s%s: name may contain only lowercase letters, digits and underscores
What it means
During schema validation, a nested block type name does not match the required pattern ^[a-z0-9_]+$. Terraform requires block type names to contain only lowercase letters, digits, and underscores — no uppercase, hyphens, dots, or other characters. This ensures names map cleanly to HCL identifiers and provider SDK conventions.
Solutions
- Rename the nested block type to use only lowercase letters, digits, and underscores (e.g., 'SubnetGroup' → 'subnet_group').
- If the name must reflect a hyphenated API field, use underscores: 'load-balancer' → 'load_balancer'.
- If using terraform-plugin-framework, the schema definitions naturally enforce this; check for string literals that bypass the framework.
- Add a unit test calling InternalValidate() to catch naming violations before release.
Example fix
// before — invalid block name with hyphen
BlockTypes: map[string]*NestedBlock{
"sub-net": {Nesting: ListNesting, ...}, // ← hyphen not allowed
}
// after — snake_case
BlockTypes: map[string]*NestedBlock{
"sub_net": {Nesting: ListNesting, ...},
} Defensive patterns
Strategy: validation
Validate before calling
// Provider developers: enforce naming convention on block types
import "regexp"
var validBlockName = regexp.MustCompile(`^[a-z0-9_]+$`)
func validateBlockTypeNames(b *configschema.Block) error {
for name := range b.BlockTypes {
if !validBlockName.MatchString(name) {
return fmt.Errorf("block type name %q must be lowercase letters, digits, and underscores only", name)
}
}
return b.InternalValidate()
}
// Unit test gate:
func TestBlockNamesConform(t *testing.T) {
if err := myResourceSchema().InternalValidate(); err != nil {
t.Fatal(err)
}
} Prevention
- Use snake_case for all block type names — never camelCase, PascalCase, kebab-case, or dotted names.
- When auto-generating schemas from API specs, sanitize names with a name-normalization function.
- Add InternalValidate() to provider test suites to catch naming violations before release.
When it happens
Trigger: A key in Block.BlockTypes contains characters outside [a-z0-9_]. Triggered when a provider developer names a nested block with uppercase letters (e.g., 'Subnet'), hyphens (e.g., 'sub-net'), dots (e.g., 'sub.net'), or any other non-conforming character.
Common situations: Provider developer mirrors an upstream API field name verbatim that contains hyphens or camelCase (common in cloud provider APIs like AWS, GCP). Schema auto-generation from an API spec that doesn't sanitize names. Copy-paste from API documentation. Developer unfamiliar with Terraform naming conventions uses PascalCase or kebab-case.
Related errors
- : all attributes within computed blocks must also be…
- : attribute schema is nil
- : block schema is nil
- : name defined as both attribute and child block type
- top-level block: DeprecationMessage must not be set when…
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/c10018c3f8fb558e.
Report an issue: GitHub.
Appendix: source
Thrown at internal/configs/configschema/internal_validate.go:58
}
multiErr = errors.Join(multiErr, attrS.internalValidate(name, prefix))
// all attributes within a computed block must also be computed
if b.Computed && !attrS.Computed {
multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: all attributes within computed blocks must also be computed", prefix, name))
}
}
for name, blockS := range b.BlockTypes {
if blockS == nil {
multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: block schema is nil", prefix, name))
continue
}
if _, isAttr := b.Attributes[name]; isAttr {
multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: name defined as both attribute and child block type", prefix, name))
} else if !validName.MatchString(name) {
multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name))
}
if !blockS.Deprecated && blockS.DeprecationMessage != "" {
multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: DeprecationMessage must not be set when Deprecated is false", prefix, name))
}
if blockS.MinItems < 0 || blockS.MaxItems < 0 {
multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: MinItems and MaxItems must both be greater than zero", prefix, name))
}
// any nested blocks within a computed block must also be computed
if b.Computed && !blockS.Computed {
multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: all nested blocks within computed blocks must also be computed", prefix, name))
}
switch blockS.Nesting {
case NestingSingle:
switch {
case blockS.MinItems != blockS.MaxItems:View on GitHub (pinned to d32a084675)