{"record":{"id":"ca4b649001d38c1b","repo":"hashicorp/terraform","slug":"s-s-name-defined-as-both-attribute-and-child-blo","errorCode":null,"errorMessage":"%s%s: name defined as both attribute and child block type","messagePattern":"(.+?)(.+?): name defined as both attribute and child block type","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/configs/configschema/internal_validate.go","lineNumber":56,"sourceCode":"\t\t\tmultiErr = errors.Join(multiErr, fmt.Errorf(\"%s%s: attribute schema is nil\", prefix, name))\n\t\t\tcontinue\n\t\t}\n\t\tmultiErr = errors.Join(multiErr, attrS.internalValidate(name, prefix))\n\n\t\t// all attributes within a computed block must also be computed\n\t\tif b.Computed && !attrS.Computed {\n\t\t\tmultiErr = errors.Join(multiErr, fmt.Errorf(\"%s%s: all attributes within computed blocks must also be computed\", prefix, name))\n\t\t}\n\t}\n\n\tfor name, blockS := range b.BlockTypes {\n\t\tif blockS == nil {\n\t\t\tmultiErr = errors.Join(multiErr, fmt.Errorf(\"%s%s: block schema is nil\", prefix, name))\n\t\t\tcontinue\n\t\t}\n\n\t\tif _, isAttr := b.Attributes[name]; isAttr {\n\t\t\tmultiErr = errors.Join(multiErr, fmt.Errorf(\"%s%s: name defined as both attribute and child block type\", prefix, name))\n\t\t} else if !validName.MatchString(name) {\n\t\t\tmultiErr = errors.Join(multiErr, fmt.Errorf(\"%s%s: name may contain only lowercase letters, digits and underscores\", prefix, name))\n\t\t}\n\t\tif !blockS.Deprecated && blockS.DeprecationMessage != \"\" {\n\t\t\tmultiErr = errors.Join(multiErr, fmt.Errorf(\"%s%s: DeprecationMessage must not be set when Deprecated is false\", prefix, name))\n\t\t}\n\n\t\tif blockS.MinItems < 0 || blockS.MaxItems < 0 {\n\t\t\tmultiErr = errors.Join(multiErr, fmt.Errorf(\"%s%s: MinItems and MaxItems must both be greater than zero\", prefix, name))\n\t\t}\n\n\t\t// any nested blocks within a computed block must also be computed\n\t\tif b.Computed && !blockS.Computed {\n\t\t\tmultiErr = errors.Join(multiErr, fmt.Errorf(\"%s%s: all nested blocks within computed blocks must also be computed\", prefix, name))\n\t\t}\n\n\t\tswitch blockS.Nesting {\n\t\tcase NestingSingle:","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/configs/configschema/internal_validate.go#L38-L74","documentation":"During schema validation, the same name appears as both a key in Block.Attributes and a key in Block.BlockTypes. Terraform requires attribute names and nested block type names to be disjoint — a given name can be either an attribute or a block type, not both. This ambiguity would make HCL parsing and plan diffing nondeterministic.","triggerScenarios":"Block.Attributes and Block.BlockTypes share a common key. Triggered when a provider schema defines, for example, an attribute named \"tags\" and also a nested block type named \"tags\" within the same parent block.","commonSituations":"Provider developer refactors an attribute into a block (or vice versa) but forgets to remove the old definition. Schema is composed from multiple sources (mixins, embedded structs) that both define the same name. Name collision from an auto-generated schema where a dynamic attribute and dynamic block use the same key. Migration between schema frameworks that handle naming differently.","solutions":["Identify the conflicting name from the error message and decide whether it should be an attribute or a nested block.","Remove the duplicate definition from either Attributes or BlockTypes so the name appears in only one map.","Rename one of them if both are genuinely needed (e.g., tags attribute vs tags_block nested block).","Add a unit test calling InternalValidate() to prevent regressions."],"exampleFix":"// before — name in both maps\nAttributes: map[string]*Attribute{\n    \"config\": {Type: String, Optional: true},\n}\nBlockTypes: map[string]*NestedBlock{\n    \"config\": {Nesting: ListNesting, ...},  // ← collision\n}\n\n// after — rename to disambiguate\nAttributes: map[string]*Attribute{\n    \"config_str\": {Type: String, Optional: true},\n}\nBlockTypes: map[string]*NestedBlock{\n    \"config\": {Nesting: ListNesting, ...},\n}","handlingStrategy":"validation","validationCode":"// Provider developers: check for name collisions before returning schema\nfunc checkNoNameCollisions(b *configschema.Block) error {\n    for name := range b.BlockTypes {\n        if _, exists := b.Attributes[name]; exists {\n            return fmt.Errorf(\"name %q is defined as both attribute and block type\", name)\n        }\n    }\n    return nil\n}\n\n// Unit test gate:\nfunc TestNoNameCollisions(t *testing.T) {\n    if err := myResourceSchema().InternalValidate(); err != nil {\n        t.Fatal(err)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When refactoring an attribute to a block or vice versa, remove the old definition entirely.","Use a shared name registry or linter to prevent collisions in composed schemas.","Gate every schema change behind an InternalValidate() unit test."],"tags":["schema","validation","provider-development","naming-collision","internal"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}