hashicorp/nomad · error

error parsing 'consul': %w

Error message

error parsing 'consul': %w

What it means

ParseConfigFile filters 'consul' blocks and passes them to parseConsuls; this error wraps any failure from that function. parseConsuls requires each consul block to be an object and decodes nested service_identity/template_identity blocks.

Source

Thrown at command/agent/config_parse.go:96

	// need to parse by hand because we don't have a label on the block
	root, err := hcl.Parse(buf.String())
	if err != nil {
		return nil, fmt.Errorf("failed to parse HCL file %s: %w", path, err)
	}
	list, ok := root.Node.(*ast.ObjectList)
	if !ok {
		return nil, fmt.Errorf("error parsing: root should be an object")
	}
	matches := list.Filter("vault")
	if len(matches.Items) > 0 {
		if err := parseVaults(c, matches); err != nil {
			return nil, fmt.Errorf("error parsing 'vault': %w", err)
		}
	}
	matches = list.Filter("consul")
	if len(matches.Items) > 0 {
		if err := parseConsuls(c, matches); err != nil {
			return nil, fmt.Errorf("error parsing 'consul': %w", err)
		}
	}

	matches = list.Filter("keyring")
	if len(matches.Items) > 0 {
		if err := parseKeyringConfigs(c, matches); err != nil {
			return nil, fmt.Errorf("error parsing 'keyring': %w", err)
		}
	}

	// convert strings to time.Durations
	tds := []durationConversionMap{
		{"gc_interval", &c.Client.GCInterval, &c.Client.GCIntervalHCL, nil},
		{"acl.token_ttl", &c.ACL.TokenTTL, &c.ACL.TokenTTLHCL, nil},
		{"acl.policy_ttl", &c.ACL.PolicyTTL, &c.ACL.PolicyTTLHCL, nil},
		{"acl.policy_ttl", &c.ACL.RoleTTL, &c.ACL.RoleTTLHCL, nil},
		{"acl.token_min_expiration_ttl", &c.ACL.TokenMinExpirationTTL, &c.ACL.TokenMinExpirationTTLHCL, nil},
		{"acl.token_max_expiration_ttl", &c.ACL.TokenMaxExpirationTTL, &c.ACL.TokenMaxExpirationTTLHCL, nil},

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Write `consul` as a braced block: `consul { address = "127.0.0.1:8500" }`
  2. Verify nested service_identity/template_identity blocks are objects with valid syntax
  3. Run a syntax check / the agent with the config to get the wrapped underlying error message
  4. Restore the consul stanza from a known-good template

Example fix

// before
consul = "127.0.0.1:8500"

// after
consul {
  address = "127.0.0.1:8500"
}
Defensive patterns

Strategy: validation

Validate before calling

func validateConsulBlock(data []byte) error {
	root, err := hcl.Parse(string(data))
	if err != nil {
		return err
	}
	list, ok := root.Node.(*ast.ObjectList)
	if !ok {
		return fmt.Errorf("root is not an object")
	}
	for _, item := range list.Filter("consul").Items {
		if _, ok := item.Val.(*ast.ObjectType); !ok {
			return fmt.Errorf("consul must be a braced block")
		}
	}
	return nil
}

Type guard

func isConsulBlock(obj *ast.ObjectItem) bool {
	_, ok := obj.Val.(*ast.ObjectType)
	return ok
}

Try / catch

cfg, err := ParseConfigFile(path)
if err != nil {
	if strings.Contains(err.Error(), "error parsing 'consul'") {
		return fmt.Errorf("invalid consul stanza in %s: %w", path, err)
	}
	return err
}

Prevention

When it happens

Trigger: Calling ParseConfigFile/LoadConfig on a config with a `consul { ... }` block whose value is not an object type (e.g. `consul = 5`), or whose nested service_identity/template_identity blocks fail hcl.DecodeObject.

Common situations: Typo turning a consul block into an assignment; YAML-to-HCL conversion producing wrong syntax; editing consul stanza by hand and dropping braces.

Related errors


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