{"record":{"id":"9c8a93357558c39a","repo":"hashicorp/nomad","slug":"error-parsing-root-should-be-an-object-9c8a93","errorCode":null,"errorMessage":"error parsing: root should be an object","messagePattern":"error parsing: root should be an object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"command/agent/config_parse.go","lineNumber":85,"sourceCode":"\t\tTelemetry: &Telemetry{},\n\t\tVaults:    []*config.VaultConfig{},\n\t\tReporting: config.DefaultReporting(),\n\t}\n\n\terr = hcl.Decode(c, buf.String())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode HCL file %s: %w\", path, err)\n\t}\n\n\t// Re-parse the file to extract the multiple Vault configurations, which we\n\t// need to parse by hand because we don't have a label on the block\n\troot, err := hcl.Parse(buf.String())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse HCL file %s: %w\", path, err)\n\t}\n\tlist, ok := root.Node.(*ast.ObjectList)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"error parsing: root should be an object\")\n\t}\n\tmatches := list.Filter(\"vault\")\n\tif len(matches.Items) > 0 {\n\t\tif err := parseVaults(c, matches); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error parsing 'vault': %w\", err)\n\t\t}\n\t}\n\tmatches = list.Filter(\"consul\")\n\tif len(matches.Items) > 0 {\n\t\tif err := parseConsuls(c, matches); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error parsing 'consul': %w\", err)\n\t\t}\n\t}\n\n\tmatches = list.Filter(\"keyring\")\n\tif len(matches.Items) > 0 {\n\t\tif err := parseKeyringConfigs(c, matches); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error parsing 'keyring': %w\", err)","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/command/agent/config_parse.go#L67-L103","documentation":"ParseConfigFile parses an agent config file twice: first via hcl.Decode into the config struct, then via hcl.Parse to hand-extract 'vault', 'consul', and 'keyring' blocks. This error is returned when the second hcl.Parse result's root AST node is not an *ast.ObjectList, meaning the file's top level is not an object/block structure HCL can filter.","triggerScenarios":"Calling ParseConfigFile (directly or via LoadConfig/LoadConfigDir) on an HCL file whose top-level node parses to something other than an ObjectList (e.g. a bare scalar or list at the top level, or a malformed/empty file producing an unexpected AST root).","commonSituations":"Config file containing only a JSON array, a bare value like \"abc\", or a file corrupted/truncated so hcl.Parse returns a non-object root; also seen when a non-HCL file is passed as a config path.","solutions":["Open the config file and ensure the top level is an HCL object/block structure (e.g. `vault { ... }` or key = value at root), not a bare value or array","If the file is JSON, wrap contents in a top-level object `{ ... }` instead of an array or scalar","Verify the path passed to LoadConfig points to a real HCL config file, not a directory or binary","Re-save the file as UTF-8 without a BOM, which can confuse the HCL parser"],"exampleFix":"// before (config.hcl)\n[\"vault\", \"consul\"]\n\n// after\nvault {}\nconsul {}","handlingStrategy":"validation","validationCode":"func validateHCLRoot(path string) error {\n\tdata, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttrimmed := bytes.TrimSpace(data)\n\tif len(trimmed) == 0 {\n\t\treturn fmt.Errorf(\"%s is empty\", path)\n\t}\n\tif trimmed[0] == '[' || trimmed[0] == '\"' || trimmed[0] == '-' {\n\t\treturn fmt.Errorf(\"%s: top-level value is not an HCL object\", path)\n\t}\n\treturn nil\n}","typeGuard":"func isHCLObjectNode(n ast.Node) bool {\n\t_, ok := n.(*ast.ObjectList)\n\treturn ok\n}","tryCatchPattern":"cfg, err := ParseConfigFile(path)\nif err != nil {\n\tif strings.Contains(err.Error(), \"root should be an object\") {\n\t\treturn fmt.Errorf(\"config %s has no top-level object; check file contents\", path)\n\t}\n\treturn err\n}","preventionTips":["Keep config files as HCL objects/blocks at the top level; never a bare value or array","Lint config files with an HCL parser in CI before deployment","Verify file paths point to files, not directories or binaries","Re-save files as UTF-8 without BOM"],"tags":["hcl","config-parsing","vault-agent"],"backgroundTag":"hcl-root-not-object","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"}