hashicorp/terraform · error

%s%s: must set Optional, Required or Computed

Error message

%s%s: must set Optional, Required or Computed

What it means

Raised when an Attribute has none of Optional, Required, or Computed set. Every attribute must declare its lifecycle so Terraform knows whether the user, the provider, or both supply the value; an undeclared attribute is ambiguous. The check lives at internal_validate.go:146.

Source

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

// schema definitions have any inconsistencies with the documented rules for
// valid schema.
func (a *Attribute) InternalValidate(name string) error {
	if a == nil {
		return fmt.Errorf("attribute schema is nil")
	}
	return a.internalValidate(name, "")
}

func (a *Attribute) internalValidate(name, prefix string) error {
	var err error

	/* FIXME: this validation breaks certain existing providers and cannot be enforced without coordination.
	if !validName.MatchString(name) {
		err = errors.Join(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name))
	}
	*/
	if !a.Optional && !a.Required && !a.Computed {
		err = errors.Join(err, fmt.Errorf("%s%s: must set Optional, Required or Computed", prefix, name))
	}
	if a.Optional && a.Required {
		err = errors.Join(err, fmt.Errorf("%s%s: cannot set both Optional and Required", prefix, name))
	}
	if a.Computed && a.Required {
		err = errors.Join(err, fmt.Errorf("%s%s: cannot set both Computed and Required", prefix, name))
	}
	if !a.Deprecated && a.DeprecationMessage != "" {
		err = errors.Join(err, fmt.Errorf("%s%s: DeprecationMessage must not be set when Deprecated is false", prefix, name))
	}

	if a.Type == cty.NilType && a.NestedType == nil {
		err = errors.Join(err, fmt.Errorf("%s%s: either Type or NestedType must be defined", prefix, name))
	}

	if a.Type != cty.NilType {
		if a.NestedType != nil {
			err = errors.Join(fmt.Errorf("%s: Type and NestedType cannot both be set", name))

View on GitHub (pinned to c9def3e214)

Solutions

  1. Set exactly one of Optional, Required, or Computed (or Optional+Computed for provider-overridable defaults).
  2. If the attribute is purely informational from the provider, use Computed: true.
  3. Run InternalValidate in provider unit tests so missing flags fail fast.

Example fix

// before
"name": {Type: cty.String},

// after
"name": {Type: cty.String, Optional: true, Computed: true}
Defensive patterns

Strategy: validation

Validate before calling

func assertAttributeLifecycle(a *configschema.Attribute) error {
    if !a.Optional && !a.Required && !a.Computed {
        return fmt.Errorf("attribute must set Optional, Required, or Computed")
    }
    return nil
}

Type guard

func hasLifecycleFlag(a *configschema.Attribute) bool {
    return a.Optional || a.Required || a.Computed
}

Prevention

When it happens

Trigger: An Attribute struct literal with Type set but Optional, Required, and Computed all left as zero-value false. internalValidate at line 138 reaches line 146 and the conjunction is true.

Common situations: Adding a new attribute and forgetting its lifecycle flag; relying on a helper that returns an Attribute without setting any flag; migrating from a schema DSL that inferred optionality implicitly.

Related errors


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