hashicorp/nomad · error
error parsing: root should be an object
Error message
error parsing: root should be an object
What it means
csiDecodeVolume parses the HCL file passed to `nomad volume register/create` into an api.CSIVolume. Before decoding, it type-asserts the parsed AST root to *ast.ObjectList; if the file's top level is not an object/list node (e.g. it parsed as a literal or empty/degenerate node), it refuses with this error. It means the volume spec file is not structured as HCL object blocks like `id = ...` / `volume { ... }` at the root.
Source
Thrown at command/volume_register_csi.go:52
c.Colorize().Color(
fmt.Sprintf("[bold][yellow]Volume Warnings:\n%s[reset]\n", resp.Warnings)))
}
for _, vol := range resp.Volumes {
// note: the command only ever returns 1 volume from the API
c.Ui.Output(fmt.Sprintf("Volume %q registered", vol.ID))
}
return 0
}
func csiDecodeVolume(input *ast.File) (*api.CSIVolume, error) {
var err error
vol := &api.CSIVolume{}
list, ok := input.Node.(*ast.ObjectList)
if !ok {
return nil, fmt.Errorf("error parsing: root should be an object")
}
// Decode the full thing into a map[string]interface for ease
var m map[string]any
err = hcl.DecodeObject(&m, list)
if err != nil {
return nil, err
}
// Need to manually parse these fields
delete(m, "capability")
delete(m, "mount_options")
delete(m, "capacity_max")
delete(m, "capacity_min")
delete(m, "topology_request")
delete(m, "type")
// Decode the restView on GitHub (pinned to 482b49bf1a)
Solutions
- Open the volume spec file and ensure its root is HCL object syntax (key/value pairs or blocks), e.g. `id = "ebs1"`, `name = "..."`, `type = "csi"`, `external_id = "..."`, `capability { access_mode = "..." access_type = "..." }`.
- Verify you are passing the correct file path (not a JSON job file or another artifact) to `nomad volume register`.
- If the file is JSON, convert it to HCL object syntax or use a Nomad version/API that accepts the format you intend.
- Validate the file parses as expected with `hcl2` tooling or by loading it in a minimal parser before registering.
Example fix
// before (file contents: plain JSON)
{"id": "ebs1"}
// after (HCL object root)
id = "ebs1"
name = "my-volume"
type = "csi"
external_id = "vol-123"
capability {
access_mode = "single-node-writer"
access_type = "single-node-writer"
} Defensive patterns
Strategy: validation
Validate before calling
// before registering, ensure the file root is HCL object syntax
func isHCLObject(t string) bool {
f, err := hcl.Parse(t)
if err != nil { return false }
_, ok := f.Node.(*ast.ObjectList)
return ok
}
if !isHCLObject(string(specBytes)) {
return fmt.Errorf("volume spec must be HCL with an object root")
} Type guard
func asObjectList(f *ast.File) (*ast.ObjectList, bool) {
ol, ok := f.Node.(*ast.ObjectList)
return ol, ok
} Try / catch
vol, err := csiDecodeVolume(f)
if err != nil {
if strings.Contains(err.Error(), "root should be an object") {
return fmt.Errorf("spec file is not HCL object syntax; check file contents")
}
return err
} Prevention
- Keep volume specs as .hcl files and never pass JSON or binary files to `nomad volume register`.
- Lint specs with an HCL parser before registering.
- Ensure the file is not empty or comment-only.
When it happens
Trigger: Running `nomad volume register <file>` or `nomad volume create <file>` where the file's HCL parse produces a root node that is not an ast.ObjectList — e.g. the file contains only a quoted string, a number, a comment, or is otherwise not object-shaped HCL.
Common situations: Accidentally registering the wrong file (a JSON blob, a certificate, a binary) as a volume spec; a file with only comments; a truncated or corrupted volume HCL file; passing a JSON file where HCL expected object syntax in older Nomad versions.
Related errors
- invalid capacity_min: %v
- invalid capacity_max: %v
- error parsing: root should be an object
- error parsing 'vault': %w
- error parsing 'consul': %w
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/a8007ea344f56a2d.
Report an issue: GitHub.