{"record":{"id":"64ff85a4bde483bb","repo":"hashicorp/nomad","slug":"path-cannot-contain-template-delimiters-or-parenth","errorCode":null,"errorMessage":"path cannot contain template delimiters or parenthesis","messagePattern":"path cannot contain template delimiters or parenthesis","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/allocrunner/taskrunner/secrets/nomad_provider.go","lineNumber":80,"sourceCode":"\t\tn.secret.Path, n.config.Namespace, n.secret.Name)\n\n\treturn &structs.Template{\n\t\tEmbeddedTmpl: data,\n\t\tDestPath:     filepath.Clean(filepath.Join(n.secretDir, n.tmplFile)),\n\t\tChangeMode:   structs.TemplateChangeModeNoop,\n\t\tOnce:         true,\n\t}\n}\n\n// validateNomadInputs ensures none of the user provided inputs contain delimiters\n// that could be used to inject other CT functions.\nfunc validateNomadInputs(conf *nomadProviderConfig, path string) error {\n\tif strings.ContainsAny(conf.Namespace, \"(){}\") {\n\t\treturn errors.New(\"namespace cannot contain template delimiters or parenthesis\")\n\t}\n\n\tif strings.ContainsAny(path, \"(){}\") {\n\t\treturn errors.New(\"path cannot contain template delimiters or parenthesis\")\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":62,"sourceCodeEnd":85,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/allocrunner/taskrunner/secrets/nomad_provider.go#L62-L85","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Strip or replace ( ) { } from the secret path in the secrets config","Percent-encode special characters if the backing store path uses them, per the provider's encoding rules","Restructure the path so delimiters are not needed (use nested paths or dashes instead of parentheses)","Verify any intermediate templating (Nomad variable interpolation) isn't leaving literal braces in the final path"],"exampleFix":"// before\npath = \"secret/data/app(default)\"\n// after\npath = \"secret/data/app-default\"","handlingStrategy":"validation","validationCode":"// Go: pre-validate the secret path before provider creation\nfunc validSecretPath(p string) bool {\n\treturn p != \"\" && !strings.ContainsAny(p, \"(){}\")\n}\n// usage\nif !validSecretPath(path) {\n\treturn errors.New(\"path must not contain ( ) { }\")\n}","typeGuard":"func isDelimiterFree(s string) bool { return !strings.ContainsAny(s, \"(){}\") }","tryCatchPattern":"p, err := NewNomadProvider(ctx, secret, dir)\nif err != nil {\n\tif strings.Contains(err.Error(), \"path cannot contain\") {\n\t\treturn nil, fmt.Errorf(\"rewrite secret path without delimiters: %w\", err)\n\t}\n\treturn nil, err\n}","preventionTips":["Use dashes/underscores instead of parentheses in KV paths and URLs","Encode special characters per provider rules instead of embedding them raw","Add a config lint step that flags template delimiters inside path fields"],"tags":["validation","template-injection","secrets","path"],"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"}