hashicorp/nomad · error

error parsing 'vault': %w

Error message

error parsing 'vault': %w

What it means

After the root AST is validated, ParseConfigFile filters out 'vault' blocks and passes them to parseVaults. This error wraps any failure from parseVaults, which itself validates block shape (must be an object) and decodes default_identity sub-blocks via hcl.DecodeObject.

Source

Thrown at command/agent/config_parse.go:90

	err = hcl.Decode(c, buf.String())
	if err != nil {
		return nil, fmt.Errorf("failed to decode HCL file %s: %w", path, err)
	}

	// Re-parse the file to extract the multiple Vault configurations, which we
	// 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{

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Ensure `vault` is written as a block with braces: `vault { address = "..." }`, not `vault = "..."`
  2. Check nested blocks inside vault (e.g. default_identity) are objects with valid HCL syntax
  3. Validate the file with `vault agent -config=<file>` or hcl linter before deploying
  4. Compare against a known-good example config from the vault docs

Example fix

// before
vault = "http://127.0.0.1:8200"

// after
vault {
  address = "http://127.0.0.1:8200"
}
Defensive patterns

Strategy: validation

Validate before calling

func validateVaultBlock(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("vault").Items {
		if _, ok := item.Val.(*ast.ObjectType); !ok {
			return fmt.Errorf("vault must be a braced block")
		}
	}
	return nil
}

Type guard

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

Try / catch

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

Prevention

When it happens

Trigger: Calling ParseConfigFile/LoadConfig on a config containing a `vault { ... }` block that fails parsing — typically a vault block whose value is not an object (e.g. `vault = "foo"`) or whose nested default_identity block fails hcl.DecodeObject.

Common situations: Hand-edited config where `vault` was accidentally assigned a scalar or list instead of a block; copy-paste errors merging vault stanzas; wrong HCL syntax after a version upgrade of the config format.

Related errors


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