hashicorp/nomad · error

cannot check HCL keys of type %T

Error message

cannot check HCL keys of type %T

What it means

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.

Source

Thrown at helper/funcs.go:249

	clean := invalidFilenameNonASCII.ReplaceAllLiteralString(filename, replace)
	return clean
}

// CleanFilenameStrict replaces invalid and punctuation characters in filename
func CleanFilenameStrict(filename string, replace string) string {
	clean := invalidFilenameStrict.ReplaceAllLiteralString(filename, replace)
	return clean
}

func CheckHCLKeys(node ast.Node, valid []string) error {
	var list *ast.ObjectList
	switch n := node.(type) {
	case *ast.ObjectList:
		list = n
	case *ast.ObjectType:
		list = n.List
	default:
		return fmt.Errorf("cannot check HCL keys of type %T", n)
	}

	validMap := make(map[string]struct{}, len(valid))
	for _, v := range valid {
		validMap[v] = struct{}{}
	}

	var result error
	for _, item := range list.Items {
		key := item.Keys[0].Token.Value().(string)
		if _, ok := validMap[key]; !ok {
			result = multierror.Append(result, fmt.Errorf(
				"invalid key: %s", key))
		}
	}

	return result
}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Pass the correct node: use the *ast.ObjectType from the parsed block (or its .List *ast.ObjectList) rather than a literal/list node
  2. Verify the caller obtained the node from hcl.Parse / the ObjectItem's Val (*ast.ObjectType) and not from decoding the value
  3. Guard for nil before calling CheckHCLKeys; a nil node also falls into the default branch
  4. If you genuinely need to validate other node kinds, extend the switch to handle them explicitly instead of hitting the default

Example fix

// before
node, _ := hcl.Parse(string(content))
// node is *ast.File, not an ObjectType
err := CheckHCLKeys(node, validKeys)
// after
objList, _ := hcl.Parse(string(content))
for _, item := range objList.Node.(*ast.ObjectList).Items {
    if err := CheckHCLKeys(item.Val.(*ast.ObjectType), validKeys); err != nil {
        return err
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

func isCheckableNode(n ast.Node) bool {
    switch n.(type) {
    case *ast.ObjectList, *ast.ObjectType:
        return true
    default:
        return false
    }
}
// call: if isCheckableNode(node) { CheckHCLKeys(node, valid) }

Type guard

func isCheckableNode(n ast.Node) bool {
    switch n.(type) {
    case *ast.ObjectList, *ast.ObjectType:
        return true
    default:
        return false
    }
}

Try / catch

if err := CheckHCLKeys(node, validKeys); err != nil {
    if strings.HasPrefix(err.Error(), "cannot check HCL keys of type") {
        return fmt.Errorf("bug: CheckHCLKeys called with non-object node: %w", err)
    }
    return err // invalid-key errors are real config problems
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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