hashicorp/nomad · error
expected map/object cty value, got %s
Error message
expected map/object cty value, got %s
What it means
CtyValueToMapInterface only accepts cty map or object types, since its return type is map[string]any. If the value is a list, tuple, string, number, etc., it fails with the type's FriendlyName in the message.
Source
Thrown at helper/pluginutils/hclutils/util.go:82
// CtyValueToMapInterface converts a decoded cty value into a Go
// map[string]interface{}.
//
// ParseHclInterface returns a cty.Value and callers sometimes need a generic
// map payload (for example for plugin config maps). This helper converts that
// value recursively into native Go values.
func CtyValueToMapInterface(val cty.Value) (map[string]any, error) {
if !val.IsKnown() {
return nil, fmt.Errorf("value is not known")
}
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()
View on GitHub (pinned to 482b49bf1a)
Solutions
- Pass a cty map/object value; wrap non-map values in an object or change the call site to a suitable converter.
- Check val.Type().IsMapType() || val.Type().IsObjectType() before calling.
- If a list is legitimate for your use case, convert elements individually with ctyValueToInterface-style logic.
Example fix
// before
m, err := CtyValueToMapInterface(val) // val is cty.List
// after
if !val.Type().IsMapType() && !val.Type().IsObjectType() {
return errors.New("plugin config must be a map/object")
}
m, err := CtyValueToMapInterface(val) Defensive patterns
Strategy: type-guard
Validate before calling
t := val.Type()
if !t.IsMapType() && !t.IsObjectType() {
return fmt.Errorf("plugin config must be a map/object, got %s", t.FriendlyName())
} Type guard
func isMapOrObject(v cty.Value) bool {
t := v.Type()
return t.IsMapType() || t.IsObjectType()
} Try / catch
m, err := CtyValueToMapInterface(val)
if err != nil {
return fmt.Errorf("invalid plugin config: %w", err)
} Prevention
- Ensure config sources produce object/map values (not lists or scalars)
- Add schema checks before conversion
- Log FriendlyName in your own errors for faster diagnosis
When it happens
Trigger: Calling CtyValueToMapInterface with a cty.Value that is not a map/object — e.g. a list of strings, a primitive, or a tuple from ParseHclInterface output.
Common situations: Plugin config validation where the config value turned out to be a list or scalar instead of a map; passing the wrong variable to validatePluginConfig.
Related errors
- value is not known
- %s must be integer; got (%T) %[2]v
- %s must be a int64; got a (%T) %[2]v
- cannot check HCL keys of type %T
- invalid key: %s
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/ec57526f7a1005b9.
Report an issue: GitHub.