{"record":{"id":"9ec50e48e561e2ab","repo":"hashicorp/nomad","slug":"secret-path-cannot-contain-template-delimiters-or","errorCode":null,"errorMessage":"secret path cannot contain template delimiters or parenthesis","messagePattern":"secret path cannot contain template delimiters or parenthesis","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/allocrunner/taskrunner/secrets/vault_provider.go","lineNumber":49,"sourceCode":"}\n\ntype VaultProvider struct {\n\tsecret    *structs.Secret\n\tsecretDir string\n\ttmplFile  string\n\tconf      *vaultProviderConfig\n}\n\n// NewVaultProvider takes a task secret and decodes the config, overwriting the default config fields\n// with any provided fields, returning an error if the secret or secret's config is invalid.\nfunc NewVaultProvider(secret *structs.Secret, secretDir string, tmplFile string) (*VaultProvider, error) {\n\tconf := defaultVaultConfig()\n\tif err := mapstructure.Decode(secret.Config, conf); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif strings.ContainsAny(secret.Path, \"(){}\") {\n\t\treturn nil, errors.New(\"secret path cannot contain template delimiters or parenthesis\")\n\t}\n\n\treturn &VaultProvider{\n\t\tsecret:    secret,\n\t\tsecretDir: secretDir,\n\t\ttmplFile:  tmplFile,\n\t\tconf:      conf,\n\t}, nil\n}\n\nfunc (v *VaultProvider) BuildTemplate() *structs.Template {\n\tindexKey := \".Data\"\n\tif v.conf.Engine == VAULT_KV_V2 {\n\t\tindexKey = \".Data.data\"\n\t}\n\n\tdata := fmt.Sprintf(`\n\t\t{{ with secret \"%s\" }}","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/allocrunner/taskrunner/secrets/vault_provider.go#L31-L67","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nsecret {\n  path = \"kv/prod(db)\"\n}\n// after\nsecret {\n  path = \"kv/prod-db\"\n}","handlingStrategy":"validation","validationCode":"// Go: pre-validate the Vault secret path before building providers\nfunc validVaultPath(p string) bool {\n\treturn p != \"\" && !strings.ContainsAny(p, \"(){}\")\n}\n// usage\nif !validVaultPath(secret.Path) {\n\treturn errors.New(\"vault secret path must not contain ( ) { }\")\n}","typeGuard":"func isDelimiterFree(s string) bool { return !strings.ContainsAny(s, \"(){}\") }","tryCatchPattern":"vp, err := NewVaultProvider(ctx, secret, dir, logger)\nif err != nil {\n\tif strings.Contains(err.Error(), \"secret path cannot contain\") {\n\t\treturn nil, fmt.Errorf(\"vault path invalid for template rendering: %w\", err)\n\t}\n\treturn nil, err\n}","preventionTips":["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"],"tags":["validation","template-injection","vault","secrets"],"backgroundTag":"template-delimiter-injection","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}