hashicorp/terraform · error

: name defined as both attribute and child block type

Error message

%s%s: name defined as both attribute and child block type

What it means

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.

Solutions

  1. Identify the conflicting name from the error message and decide whether it should be an attribute or a nested block.
  2. Remove the duplicate definition from either Attributes or BlockTypes so the name appears in only one map.
  3. Rename one of them if both are genuinely needed (e.g., tags attribute vs tags_block nested block).
  4. Add a unit test calling InternalValidate() to prevent regressions.

Example fix

// before — name in both maps
Attributes: map[string]*Attribute{
    "config": {Type: String, Optional: true},
}
BlockTypes: map[string]*NestedBlock{
    "config": {Nesting: ListNesting, ...},  // ← collision
}

// after — rename to disambiguate
Attributes: map[string]*Attribute{
    "config_str": {Type: String, Optional: true},
}
BlockTypes: map[string]*NestedBlock{
    "config": {Nesting: ListNesting, ...},
}
Defensive patterns

Strategy: validation

Validate before calling

// Provider developers: check for name collisions before returning schema
func checkNoNameCollisions(b *configschema.Block) error {
    for name := range b.BlockTypes {
        if _, exists := b.Attributes[name]; exists {
            return fmt.Errorf("name %q is defined as both attribute and block type", name)
        }
    }
    return nil
}

// Unit test gate:
func TestNoNameCollisions(t *testing.T) {
    if err := myResourceSchema().InternalValidate(); err != nil {
        t.Fatal(err)
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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

Appendix: source

Thrown at internal/configs/configschema/internal_validate.go:56

			multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: attribute schema is nil", prefix, name))
			continue
		}
		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:

View on GitHub (pinned to d32a084675)