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 rest

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. 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 = "..." }`.
  2. Verify you are passing the correct file path (not a JSON job file or another artifact) to `nomad volume register`.
  3. If the file is JSON, convert it to HCL object syntax or use a Nomad version/API that accepts the format you intend.
  4. 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

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


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