hashicorp/terraform · error

top-level block: DeprecationMessage must not be set when Dep

Error message

top-level block: DeprecationMessage must not be set when Deprecated is false

What it means

At the top-level block only (prefix==""), DeprecationMessage is non-empty while Deprecated is false. The schema contract (documented on Block.DeprecationMessage) is that the message is valid only when Deprecated is true; setting the message without the flag is contradictory. Caught at lines 32-34 before recursing.

Source

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

// InternalValidate returns an error if the receiving block and its child schema
// definitions have any inconsistencies with the documented rules for valid
// schema.
//
// This can be used within unit tests to detect when a given schema is invalid,
// and is run when terraform loads provider schemas during NewContext.
func (b *Block) InternalValidate() error {
	if b == nil {
		return fmt.Errorf("top-level block schema is nil")
	}
	return b.internalValidate("")
}

func (b *Block) internalValidate(prefix string) error {
	var multiErr error

	if prefix == "" && !b.Deprecated && b.DeprecationMessage != "" {
		multiErr = errors.Join(multiErr, fmt.Errorf("top-level block: DeprecationMessage must not be set when Deprecated is false"))
	}

	for name, attrS := range b.Attributes {
		if attrS == nil {
			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))

View on GitHub (pinned to c9def3e214)

Solutions

  1. Set Deprecated = true alongside DeprecationMessage on the top-level Block.
  2. Or clear DeprecationMessage if the resource is not actually deprecated.
  3. Add a schema-builder helper that enforces the pairing invariant.

Example fix

// before
schema := &configschema.Block{
    DeprecationMessage: "use 'foo_v2' instead",
}

// after
schema := &configschema.Block{
    Deprecated:         true,
    DeprecationMessage: "use 'foo_v2' instead",
}
Defensive patterns

Strategy: validation

Validate before calling

// In a schema builder helper, assert the invariant:
// if DeprecationMessage != "" then Deprecated must be true.
func checkDeprecation(b *configschema.Block) error {
    if !b.Deprecated && b.DeprecationMessage != "" {
        return errors.New("DeprecationMessage set without Deprecated=true")
    }
    return nil
}

Prevention

When it happens

Trigger: A provider schema sets b.DeprecationMessage = "use x instead" but forgets b.Deprecated = true on the root block.

Common situations: Adding a deprecation notice incrementally, copy-paste from a deprecated block missing the flag, SDK auto-generation mismatch.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/69117209567f5c11. Report an issue: GitHub.