hashicorp/nomad · error

path cannot contain template delimiters or parenthesis

Error message

path cannot contain template delimiters or parenthesis

What it means

Same guard as the namespace check in validateNomadInputs (nomad_provider.go), but applied to the secret path argument. Because the path is embedded verbatim into a generated Consul Template snippet, any ( ) { } characters could inject extra template functions; the provider therefore rejects paths containing them with 'path cannot contain template delimiters or parenthesis'.

Source

Thrown at client/allocrunner/taskrunner/secrets/nomad_provider.go:80

		n.secret.Path, n.config.Namespace, n.secret.Name)

	return &structs.Template{
		EmbeddedTmpl: data,
		DestPath:     filepath.Clean(filepath.Join(n.secretDir, n.tmplFile)),
		ChangeMode:   structs.TemplateChangeModeNoop,
		Once:         true,
	}
}

// validateNomadInputs ensures none of the user provided inputs contain delimiters
// that could be used to inject other CT functions.
func validateNomadInputs(conf *nomadProviderConfig, path string) error {
	if strings.ContainsAny(conf.Namespace, "(){}") {
		return errors.New("namespace cannot contain template delimiters or parenthesis")
	}

	if strings.ContainsAny(path, "(){}") {
		return errors.New("path cannot contain template delimiters or parenthesis")
	}

	return nil
}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Strip or replace ( ) { } from the secret path in the secrets config
  2. Percent-encode special characters if the backing store path uses them, per the provider's encoding rules
  3. Restructure the path so delimiters are not needed (use nested paths or dashes instead of parentheses)
  4. Verify any intermediate templating (Nomad variable interpolation) isn't leaving literal braces in the final path

Example fix

// before
path = "secret/data/app(default)"
// after
path = "secret/data/app-default"
Defensive patterns

Strategy: validation

Validate before calling

// Go: pre-validate the secret path before provider creation
func validSecretPath(p string) bool {
	return p != "" && !strings.ContainsAny(p, "(){}")
}
// usage
if !validSecretPath(path) {
	return errors.New("path must not contain ( ) { }")
}

Type guard

func isDelimiterFree(s string) bool { return !strings.ContainsAny(s, "(){}") }

Try / catch

p, err := NewNomadProvider(ctx, secret, dir)
if err != nil {
	if strings.Contains(err.Error(), "path cannot contain") {
		return nil, fmt.Errorf("rewrite secret path without delimiters: %w", err)
	}
	return nil, err
}

Prevention

When it happens

Trigger: NewNomadProvider -> validateNomadInputs(conf, path) where the path string (e.g. "secret/data/app(env)") contains any of ( ) { }. This fires when the path in the secret stanza config includes parentheses or braces, whether hand-written, templated by another layer, or copied from a URL.

Common situations: Paths pasted from URLs or docs that include parenthesized segments, paths built by string templating where surrounding braces survived interpolation, or KV paths with special characters used in Nomad variable naming conventions.

Related errors


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