hashicorp/nomad · error

error parsing: root should be an object

Error message

error parsing: root should be an object

What it means

`nomad quota apply -parse-only` (and related parsing) reads an HCL quota specification file and expects the top-level AST node to be an ObjectList (the file's body). If the parsed root is not an object (e.g. the file contains a bare scalar, list, or is otherwise malformed), parsing fails with this error.

Source

Thrown at command/quota_apply.go:152

		c.Ui.Error(fmt.Sprintf("Error applying quota specification: %s", err))
		return 1
	}

	c.Ui.Output(fmt.Sprintf("Successfully applied quota specification %q!", spec.Name))
	return 0
}

// parseQuotaSpec is used to parse the quota specification from HCL
func parseQuotaSpec(input []byte) (*api.QuotaSpec, error) {
	root, err := hcl.ParseBytes(input)
	if err != nil {
		return nil, err
	}

	// Top-level item should be a list
	list, ok := root.Node.(*ast.ObjectList)
	if !ok {
		return nil, fmt.Errorf("error parsing: root should be an object")
	}

	var spec api.QuotaSpec
	if err := parseQuotaSpecImpl(&spec, list); err != nil {
		return nil, err
	}

	return &spec, nil
}

// parseQuotaSpecImpl parses the quota spec taking as input the AST tree
func parseQuotaSpecImpl(result *api.QuotaSpec, list *ast.ObjectList) error {
	// Check for invalid keys
	valid := []string{
		"name",
		"description",
		"limit",
	}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Ensure the quota file's top level is HCL object syntax: `name = "..."` plus `limits { ... }` blocks.
  2. If the spec is JSON, keep the .json extension so the JSON parser is used.
  3. Verify the file is non-empty and was not truncated or overwritten.
  4. Check for characters before the first key (BOM, comments in wrong format) that can make the root parse as non-object.

Example fix

// before (quota.hcl, invalid root)
["team-a"]

// after
name = "team-a"
description = "quota for team a"
limits { region = "global" }
Defensive patterns

Strategy: validation

Validate before calling

const content = fs.readFileSync(quotaFile, 'utf8');
if (content.trim() === '') throw new Error('quota file is empty');
if (quotaFile.endsWith('.json') && !content.trim().startsWith('{')) throw new Error('JSON quota must be an object at top level');
if (!quotaFile.endsWith('.json') && !/^[a-zA-Z_]+\s*=/.test(content.trim()) && !/(limits|name)\s*[={]/.test(content)) throw new Error('HCL quota must start with key/value blocks');

Prevention

When it happens

Trigger: Passing a quota HCL file whose top level is not key/value blocks — e.g. an empty file, a file containing only a JSON array, or content that HCL parses into a non-object root.

Common situations: Uploading a JSON-encoded quota (which needs the .json extension/JSON parser) with a .hcl name, an accidentally emptied file, or a file with stray syntax that breaks HCL parsing.

Related errors


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