hashicorp/terraform · critical

invalid set element type

Error message

invalid set element type

What it means

Panics in Schema.ZeroValue (schema.go:352) when building a zero-value Set for a TypeSet that has no custom Set function. It hashes elements using s.Elem and only handles *Schema and *Resource; any other concrete Elem (nil, a ValueType, a struct) panics with 'invalid set element type'.

Source

Thrown at internal/legacy/helper/schema/schema.go:352

	return nil, nil
}

// Returns a zero value for the schema.
func (s *Schema) ZeroValue() interface{} {
	// If it's a set then we'll do a bit of extra work to provide the
	// right hashing function in our empty value.
	if s.Type == TypeSet {
		setFunc := s.Set
		if setFunc == nil {
			// Default set function uses the schema to hash the whole value
			elem := s.Elem
			switch t := elem.(type) {
			case *Schema:
				setFunc = HashSchema(t)
			case *Resource:
				setFunc = HashResource(t)
			default:
				panic("invalid set element type")
			}
		}
		return &Set{F: setFunc}
	} else {
		return s.Type.Zero()
	}
}

func (s *Schema) finalizeDiff(d *terraform.ResourceAttrDiff, customized bool) *terraform.ResourceAttrDiff {
	if d == nil {
		return d
	}

	if s.Type == TypeBool {
		normalizeBoolString := func(s string) string {
			switch s {
			case "0":
				return "false"

View on GitHub (pinned to c9def3e214)

Solutions

  1. Set Elem to &schema.Schema{Type: ...} or &schema.Resource{Schema: ...}.
  2. Alternatively provide a custom Set (hash) function, which bypasses this code path entirely.
  3. Lint provider schemas for TypeSet entries lacking a valid Elem.

Example fix

// before
"roles": { Type: schema.TypeSet, Elem: schema.TypeString },
// after
"roles": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString} },
Defensive patterns

Strategy: type-guard

Validate before calling

for k, s := range r.Schema {
    if s.Type == schema.TypeSet && s.Set == nil && !isSchemaOrResource(s.Elem) {
        return fmt.Errorf("%s: TypeSet Elem must be *Schema or *Resource", k)
    }
}

Type guard

func isSchemaOrResource(v interface{}) bool {
    switch v.(type) {
    case *schema.Schema, *schema.Resource: return true
    }
    return false
}

Prevention

When it happens

Trigger: Declaring a TypeSet Schema with Elem set to a non-Schema/non-Resource value (commonly `Elem: schema.TypeString` instead of `Elem: &schema.Schema{Type: schema.TypeString}`), or Elem left nil with no custom Set func, then accessing ZeroValue (e.g. GetOk/Get on an empty set).

Common situations: Provider authors confuse Elem (which must be &schema.Schema/&schema.Resource) with Type; or omit Elem entirely on a TypeSet.

Related errors


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