hashicorp/nomad · error
secret path cannot contain template delimiters or parenthesi
Error message
secret path cannot contain template delimiters or parenthesis
What it means
The Vault secrets template provider (vault_provider.go) builds a CT snippet from the secret stanza. NewVaultProvider rejects a secret.Path containing ( ) { } because those characters could inject Consul Template functions into the generated template. It returns 'secret path cannot contain template delimiters or parenthesis' as a construction error.
Source
Thrown at client/allocrunner/taskrunner/secrets/vault_provider.go:49
}
type VaultProvider struct {
secret *structs.Secret
secretDir string
tmplFile string
conf *vaultProviderConfig
}
// NewVaultProvider takes a task secret and decodes the config, overwriting the default config fields
// with any provided fields, returning an error if the secret or secret's config is invalid.
func NewVaultProvider(secret *structs.Secret, secretDir string, tmplFile string) (*VaultProvider, error) {
conf := defaultVaultConfig()
if err := mapstructure.Decode(secret.Config, conf); err != nil {
return nil, err
}
if strings.ContainsAny(secret.Path, "(){}") {
return nil, errors.New("secret path cannot contain template delimiters or parenthesis")
}
return &VaultProvider{
secret: secret,
secretDir: secretDir,
tmplFile: tmplFile,
conf: conf,
}, nil
}
func (v *VaultProvider) BuildTemplate() *structs.Template {
indexKey := ".Data"
if v.conf.Engine == VAULT_KV_V2 {
indexKey = ".Data.data"
}
data := fmt.Sprintf(`
{{ with secret "%s" }}View on GitHub (pinned to 482b49bf1a)
Solutions
- Remove ( ) { } from the Vault secret path in the secret stanza
- Rename the Vault path/mount to use only alphanumerics, dashes, slashes, underscores
- If the character comes from upstream interpolation, fix the upstream template so only the resolved value is passed
- Keep the secret version/qualifier in Vault metadata (e.g. kv v2 version param) rather than in the path string
Example fix
// before
secret {
path = "kv/prod(db)"
}
// after
secret {
path = "kv/prod-db"
} Defensive patterns
Strategy: validation
Validate before calling
// Go: pre-validate the Vault secret path before building providers
func validVaultPath(p string) bool {
return p != "" && !strings.ContainsAny(p, "(){}")
}
// usage
if !validVaultPath(secret.Path) {
return errors.New("vault secret path must not contain ( ) { }")
} Type guard
func isDelimiterFree(s string) bool { return !strings.ContainsAny(s, "(){}") } Try / catch
vp, err := NewVaultProvider(ctx, secret, dir, logger)
if err != nil {
if strings.Contains(err.Error(), "secret path cannot contain") {
return nil, fmt.Errorf("vault path invalid for template rendering: %w", err)
}
return nil, err
} Prevention
- Keep Vault KV paths limited to alphanumerics, dashes, underscores, slashes
- Use Vault's version/metadata parameters for qualifiers instead of parentheses in paths
- CI-lint Vault paths referenced by Nomad secret stanzas for delimiter characters
When it happens
Trigger: buildSecretProviders -> NewVaultProvider with a secret stanza whose Path (from secret.Path) contains any of ( ) { }, e.g. path = "kv/app(v1)". The mapstructure decode of the secret config succeeds first; the delimiter check then fails the provider construction before any secret is fetched.
Common situations: Vault paths copied from tooling or docs that annotate with parentheses, paths assembled via Nomad template interpolation leaving residual braces, or teams using special characters in Vault KV mount/path names.
Related errors
- namespace cannot contain template delimiters or parenthesis
- path cannot contain template delimiters or parenthesis
- Secret %q has provider "vault" but no vault block
- secret name cannot be empty
- secret provider cannot be empty
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/9ec50e48e561e2ab.
Report an issue: GitHub.