{"record":{"id":"ec57526f7a1005b9","repo":"hashicorp/nomad","slug":"expected-map-object-cty-value-got-s","errorCode":null,"errorMessage":"expected map/object cty value, got %s","messagePattern":"expected map/object cty value, got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/pluginutils/hclutils/util.go","lineNumber":82,"sourceCode":"\n// CtyValueToMapInterface converts a decoded cty value into a Go\n// map[string]interface{}.\n//\n// ParseHclInterface returns a cty.Value and callers sometimes need a generic\n// map payload (for example for plugin config maps). This helper converts that\n// value recursively into native Go values.\nfunc CtyValueToMapInterface(val cty.Value) (map[string]any, error) {\n\tif !val.IsKnown() {\n\t\treturn nil, fmt.Errorf(\"value is not known\")\n\t}\n\n\tif val.IsNull() {\n\t\treturn nil, nil\n\t}\n\n\tt := val.Type()\n\tif !t.IsMapType() && !t.IsObjectType() {\n\t\treturn nil, fmt.Errorf(\"expected map/object cty value, got %s\", t.FriendlyName())\n\t}\n\n\tv, err := ctyValueToInterface(val)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tm, ok := v.(map[string]any)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"expected map/object cty value, got %T\", v)\n\t}\n\n\treturn m, nil\n}\n\nfunc ctyValueToInterface(val cty.Value) (interface{}, error) {\n\tt := val.Type()\n","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/pluginutils/hclutils/util.go#L64-L100","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nm, err := CtyValueToMapInterface(val) // val is cty.List\n// after\nif !val.Type().IsMapType() && !val.Type().IsObjectType() {\n    return errors.New(\"plugin config must be a map/object\")\n}\nm, err := CtyValueToMapInterface(val)","handlingStrategy":"type-guard","validationCode":"t := val.Type()\nif !t.IsMapType() && !t.IsObjectType() {\n    return fmt.Errorf(\"plugin config must be a map/object, got %s\", t.FriendlyName())\n}","typeGuard":"func isMapOrObject(v cty.Value) bool {\n    t := v.Type()\n    return t.IsMapType() || t.IsObjectType()\n}","tryCatchPattern":"m, err := CtyValueToMapInterface(val)\nif err != nil {\n    return fmt.Errorf(\"invalid plugin config: %w\", err)\n}","preventionTips":["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"],"tags":["cty","hcl","type-mismatch","go"],"backgroundTag":"unexpected-type","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}