{"record":{"id":"5599bf91f953660e","repo":"hashicorp/nomad","slug":"should-be-an-object","errorCode":null,"errorMessage":"should be an object","messagePattern":"should be an object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"command/agent/config_parse.go","lineNumber":502,"sourceCode":"\t\t\t\tc.Vaults[i] = exist.Merge(v)\n\t\t\t\tvaultFound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !vaultFound {\n\t\t\tc.Vaults = append(c.Vaults, v)\n\t\t}\n\n\t\tfor _, conf := range c.Vaults {\n\t\t\tconf.Addr = ipaddr.NormalizeAddr(conf.Addr)\n\t\t}\n\n\t\t// Decode the default identity.\n\t\tvar listVal *ast.ObjectList\n\t\tif ot, ok := obj.Val.(*ast.ObjectType); ok {\n\t\t\tlistVal = ot.List\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"should be an object\")\n\t\t}\n\n\t\tif o := listVal.Filter(\"default_identity\"); len(o.Items) > 0 {\n\t\t\tvar m map[string]any\n\t\t\tdefaultIdentityBlock := o.Items[0]\n\t\t\tif err := hcl.DecodeObject(&m, defaultIdentityBlock.Val); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tvar defaultIdentity config.WorkloadIdentityConfig\n\t\t\tif err := mapstructure.WeakDecode(m, &defaultIdentity); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tv.DefaultIdentity = &defaultIdentity\n\t\t}\n\t}\n\n\treturn nil","sourceCodeStart":484,"sourceCodeEnd":520,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/command/agent/config_parse.go#L484-L520","documentation":"parseVaults iterates each `vault` block and requires its value to be an *ast.ObjectType (a braced object). If the vault stanza is a scalar, list, or other non-object AST node, it returns 'should be an object', which ParseConfigFile wraps as \"error parsing 'vault': should be an object\".","triggerScenarios":"A config file where `vault` appears but its value is not a braced block — e.g. `vault = \"http://...\"` or `vault = [1,2]` — reached via ParseConfigFile/LoadConfig.","commonSituations":"Assigning a string address directly to vault instead of nesting under a block; automated config generation emitting wrong syntax; merge conflicts resolving to an assignment instead of a block.","solutions":["Change `vault = <value>` to a block: `vault { address = \"<value>\" }`","Check every `vault` occurrence in the file (multiple vault stanzas are supported; each must be an object)","Re-run the agent after fixing to confirm the wrapped error is gone"],"exampleFix":"// before\nvault = \"https://vault.service.consul:8200\"\n\n// after\nvault {\n  address = \"https://vault.service.consul:8200\"\n}","handlingStrategy":"validation","validationCode":"func assertVaultIsBlock(data []byte) error {\n\troot, err := hcl.Parse(string(data))\n\tif err != nil {\n\t\treturn err\n\t}\n\tlist, ok := root.Node.(*ast.ObjectList)\n\tif !ok {\n\t\treturn fmt.Errorf(\"root not an object\")\n\t}\n\tfor _, v := range list.Filter(\"vault\").Items {\n\t\tif _, ok := v.Val.(*ast.ObjectType); !ok {\n\t\t\treturn fmt.Errorf(\"vault stanza must be `vault { ... }`\")\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":"func isObjectType(v ast.Node) bool {\n\t_, ok := v.(*ast.ObjectType)\n\treturn ok\n}","tryCatchPattern":"cfg, err := ParseConfigFile(path)\nif err != nil {\n\tif strings.Contains(err.Error(), \"should be an object\") && strings.Contains(err.Error(), \"'vault'\") {\n\t\treturn fmt.Errorf(\"vault stanza in %s must be a block: vault { ... }\", path)\n\t}\n\treturn err\n}","preventionTips":["Never assign a scalar directly to vault; always use braces","Audit generated configs for `vault =` assignments","Keep stanzas in templates that enforce block syntax","Run the agent against configs in CI as a smoke test"],"tags":["hcl","vault","config-parsing"],"backgroundTag":"hcl-block-shape-invalid","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"}