{"record":{"id":"7195d386f9edfcec","repo":"hashicorp/nomad","slug":"cannot-check-hcl-keys-of-type-t","errorCode":null,"errorMessage":"cannot check HCL keys of type %T","messagePattern":"cannot check HCL keys of type %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/funcs.go","lineNumber":249,"sourceCode":"\tclean := invalidFilenameNonASCII.ReplaceAllLiteralString(filename, replace)\n\treturn clean\n}\n\n// CleanFilenameStrict replaces invalid and punctuation characters in filename\nfunc CleanFilenameStrict(filename string, replace string) string {\n\tclean := invalidFilenameStrict.ReplaceAllLiteralString(filename, replace)\n\treturn clean\n}\n\nfunc CheckHCLKeys(node ast.Node, valid []string) error {\n\tvar list *ast.ObjectList\n\tswitch n := node.(type) {\n\tcase *ast.ObjectList:\n\t\tlist = n\n\tcase *ast.ObjectType:\n\t\tlist = n.List\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot check HCL keys of type %T\", n)\n\t}\n\n\tvalidMap := make(map[string]struct{}, len(valid))\n\tfor _, v := range valid {\n\t\tvalidMap[v] = struct{}{}\n\t}\n\n\tvar result error\n\tfor _, item := range list.Items {\n\t\tkey := item.Keys[0].Token.Value().(string)\n\t\tif _, ok := validMap[key]; !ok {\n\t\t\tresult = multierror.Append(result, fmt.Errorf(\n\t\t\t\t\"invalid key: %s\", key))\n\t\t}\n\t}\n\n\treturn result\n}","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/funcs.go#L231-L267","documentation":"CheckHCLKeys validates that all keys of an HCL block belong to a set of valid key names. It only supports nodes that are *ast.ObjectList or *ast.ObjectType; if a caller passes any other ast.Node (e.g. *ast.LiteralType, *ast.ListType, nil), it returns this error naming the actual Go type. Nomad's parse functions (quota specs, storage/device resources, node pool limits, etc.) call it right after HCL decoding.","triggerScenarios":"Calling CheckHCLKeys (directly or via parseQuotaSpecImpl, parseQuotaLimits, parseQuotaResource, parseStorageResource, parseDeviceResource, parseNodePoolLimit) with an ast.Node that is neither an *ast.ObjectList nor *ast.ObjectType — for example passing the result of decoding a bare literal or list, or a nil node.","commonSituations":"Custom Nomad forks/plugins adding new HCL block parsing that feeds the wrong AST node type into CheckHCLKeys; refactored parsers that pass a decoded value instead of the block's ObjectType; unit tests calling CheckHCLKeys with a mocked ast.Node.","solutions":["Pass the correct node: use the *ast.ObjectType from the parsed block (or its .List *ast.ObjectList) rather than a literal/list node","Verify the caller obtained the node from hcl.Parse / the ObjectItem's Val (*ast.ObjectType) and not from decoding the value","Guard for nil before calling CheckHCLKeys; a nil node also falls into the default branch","If you genuinely need to validate other node kinds, extend the switch to handle them explicitly instead of hitting the default"],"exampleFix":"// before\nnode, _ := hcl.Parse(string(content))\n// node is *ast.File, not an ObjectType\nerr := CheckHCLKeys(node, validKeys)\n// after\nobjList, _ := hcl.Parse(string(content))\nfor _, item := range objList.Node.(*ast.ObjectList).Items {\n    if err := CheckHCLKeys(item.Val.(*ast.ObjectType), validKeys); err != nil {\n        return err\n    }\n}","handlingStrategy":"type-guard","validationCode":"func isCheckableNode(n ast.Node) bool {\n    switch n.(type) {\n    case *ast.ObjectList, *ast.ObjectType:\n        return true\n    default:\n        return false\n    }\n}\n// call: if isCheckableNode(node) { CheckHCLKeys(node, valid) }","typeGuard":"func isCheckableNode(n ast.Node) bool {\n    switch n.(type) {\n    case *ast.ObjectList, *ast.ObjectType:\n        return true\n    default:\n        return false\n    }\n}","tryCatchPattern":"if err := CheckHCLKeys(node, validKeys); err != nil {\n    if strings.HasPrefix(err.Error(), \"cannot check HCL keys of type\") {\n        return fmt.Errorf(\"bug: CheckHCLKeys called with non-object node: %w\", err)\n    }\n    return err // invalid-key errors are real config problems\n}","preventionTips":["Always pass the block's *ast.ObjectType (ObjectItem.Val) or the parsed *ast.ObjectList, never decoded values or literals","Check for nil nodes before calling CheckHCLKeys","When adding new HCL block parsers, mirror existing parse*Impl patterns that use CheckHCLKeys","In tests, construct *ast.ObjectType fixtures rather than arbitrary ast.Node mocks"],"tags":["hcl","parsing","config","type-mismatch"],"backgroundTag":"hcl-node-type-unsupported","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"}