hashicorp/nomad · error

expected map/object cty value, got %T

Error message

expected map/object cty value, got %T

What it means

After recursive conversion succeeds, CtyValueToMapInterface asserts the result is map[string]any. Although the type check on the cty side should guarantee this, the conversion layer can still yield a different Go type; this %T-based error is the defensive fallback.

Source

Thrown at helper/pluginutils/hclutils/util.go:92

	}

	if val.IsNull() {
		return nil, nil
	}

	t := val.Type()
	if !t.IsMapType() && !t.IsObjectType() {
		return nil, fmt.Errorf("expected map/object cty value, got %s", t.FriendlyName())
	}

	v, err := ctyValueToInterface(val)
	if err != nil {
		return nil, err
	}

	m, ok := v.(map[string]any)
	if !ok {
		return nil, fmt.Errorf("expected map/object cty value, got %T", v)
	}

	return m, nil
}

func ctyValueToInterface(val cty.Value) (interface{}, error) {
	t := val.Type()

	if val.IsNull() {
		return nil, nil
	}

	if !val.IsKnown() {
		return nil, fmt.Errorf("value is not known")
	}

	switch {
	case t.IsPrimitiveType():

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Inspect the printed Go type (%T) and fix the converter or input so it produces map[string]any.
  2. Ensure the input value really is a cty map/object before calling (the earlier check filters most cases).
  3. If you own the code, extend ctyValueToInterface to map the offending type correctly.

Example fix

// before
v, _ := ctyValueToInterface(val)
m := v.(map[string]any)
// after
m, ok := v.(map[string]any)
if !ok {
    return fmt.Errorf("unexpected converted type %T", v)
}
Defensive patterns

Strategy: type-guard

Validate before calling

v, err := ctyValueToInterface(val)
if err != nil {
    return err
}
if _, ok := v.(map[string]any); !ok {
    return fmt.Errorf("converter produced %T, expected map[string]any", v)
}

Type guard

m, ok := v.(map[string]any)
if !ok {
    return fmt.Errorf("expected map[string]any, got %T", v)
}

Try / catch

m, err := CtyValueToMapInterface(val)
if err != nil {
    return fmt.Errorf("plugin config conversion failed: %w", err)
}

Prevention

When it happens

Trigger: ctyValueToInterface returns a non-map Go value (defensive path; normally unreachable when the input passed the map/object type check, e.g. due to future changes in the converter).

Common situations: Rare — mostly hit after code changes to the recursive converter or when capsule/custom types produce unusual Go values; surfaced in tests like TestCtyValueToMapInterface_InvalidType.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/c5beae3db1e74c02. Report an issue: GitHub.