{"record":{"id":"c2ee41ee8fd074a1","repo":"hashicorp/terraform","slug":"keys-and-searchset-must-be-of-the-same-type","errorCode":null,"errorMessage":"keys and searchset must be of the same type","messagePattern":"keys and searchset must be of the same type","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/lang/funcs/collection.go","lineNumber":352,"sourceCode":"var MatchkeysFunc = function.New(&function.Spec{\n\tParams: []function.Parameter{\n\t\t{\n\t\t\tName: \"values\",\n\t\t\tType: cty.List(cty.DynamicPseudoType),\n\t\t},\n\t\t{\n\t\t\tName: \"keys\",\n\t\t\tType: cty.List(cty.DynamicPseudoType),\n\t\t},\n\t\t{\n\t\t\tName: \"searchset\",\n\t\t\tType: cty.List(cty.DynamicPseudoType),\n\t\t},\n\t},\n\tType: func(args []cty.Value) (cty.Type, error) {\n\t\tty, _ := convert.UnifyUnsafe([]cty.Type{args[1].Type(), args[2].Type()})\n\t\tif ty == cty.NilType {\n\t\t\treturn cty.NilType, errors.New(\"keys and searchset must be of the same type\")\n\t\t}\n\n\t\t// the return type is based on args[0] (values)\n\t\treturn args[0].Type(), nil\n\t},\n\tRefineResult: refineNotNull,\n\tImpl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {\n\t\tif !args[0].IsKnown() {\n\t\t\treturn cty.UnknownVal(cty.List(retType.ElementType())), nil\n\t\t}\n\n\t\tif args[0].LengthInt() != args[1].LengthInt() {\n\t\t\treturn cty.ListValEmpty(retType.ElementType()), errors.New(\"length of keys and values should be equal\")\n\t\t}\n\n\t\toutput := make([]cty.Value, 0)\n\t\tvalues := args[0]\n","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/lang/funcs/collection.go#L334-L370","documentation":"Thrown by MatchkeysFunc.Type (the HCL matchkeys() builtin). matchkeys(values, keys, searchset) requires keys and searchset to share a type; if convert.UnifyUnsafe fails on those two, the Type function rejects the call before any matching runs.","triggerScenarios":"matchkeys(vals, [\"a\",\"b\"], [1]) (keys are strings, searchset numbers), or passing a list of one type and a set/tuple of incompatible element types.","commonSituations":"Building searchset from a different source whose element type differs; mixing string and number identifiers; a tuple vs list mismatch.","solutions":["Normalize both to the same element type: matchkeys(vals, keys, [for s in searchset : tostring(s)]).","Construct keys and searchset from the same typed list literal.","Use tolist() on both sides to ensure list-of-uniform-element types."],"exampleFix":"# before\nlocals { r = matchkeys(var.values, var.keys, [var.want_id]) }   # keys=str, want_id=num\n# after\nlocals { r = matchkeys(var.values, var.keys, [tostring(var.want_id)]) }","handlingStrategy":"type-guard","validationCode":"# unify keys and searchset element types before matchkeys\nlocals {\n  search = [for s in var.searchset : tostring(s)]\n  r      = matchkeys(var.values, var.keys, local.search)\n}","typeGuard":"locals {\n  keys_t    = [for k in var.keys : tostring(k)]\n  search_t  = [for s in var.searchset : tostring(s)]\n  r         = matchkeys(var.values, local.keys_t, local.search_t)\n}","tryCatchPattern":"locals { r = try(matchkeys(var.values, var.keys, var.searchset), []) }","preventionTips":["Build keys and searchset from the same typed source.","Use tolist() with explicit element conversions on both sides.","Type variables strictly (list(string)) so coercion is deterministic."],"tags":["hcl","function","matchkeys","type-validation"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}