{"record":{"id":"2b17789330315af5","repo":"fatedier/frp","slug":"exec-env-name-cannot-contain","errorCode":null,"errorMessage":"exec env name cannot contain '='","messagePattern":"exec env name cannot contain '='","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/v1/value_source.go","lineNumber":131,"sourceCode":"\treturn strings.TrimSpace(string(content)), nil\n}\n\n// Validate validates the ExecSource configuration.\nfunc (e *ExecSource) Validate() error {\n\tif e == nil {\n\t\treturn errors.New(\"execSource cannot be nil\")\n\t}\n\n\tif e.Command == \"\" {\n\t\treturn errors.New(\"exec command cannot be empty\")\n\t}\n\n\tfor _, env := range e.Env {\n\t\tif env.Name == \"\" {\n\t\t\treturn errors.New(\"exec env name cannot be empty\")\n\t\t}\n\t\tif strings.Contains(env.Name, \"=\") {\n\t\t\treturn errors.New(\"exec env name cannot contain '='\")\n\t\t}\n\t}\n\treturn nil\n}\n\n// Resolve reads and returns the content captured from stdout of launched subprocess.\nfunc (e *ExecSource) Resolve(ctx context.Context) (string, error) {\n\tif err := e.Validate(); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tcmd := exec.CommandContext(ctx, e.Command, e.Args...)\n\tif len(e.Env) != 0 {\n\t\tcmd.Env = os.Environ()\n\t\tfor _, env := range e.Env {\n\t\t\tcmd.Env = append(cmd.Env, env.Name+\"=\"+env.Value)\n\t\t}\n\t}","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/v1/value_source.go#L113-L149","documentation":"Thrown by ExecSource.Validate() when an env entry's Name contains '='. exec.Cmd.Env expects KEY=VALUE strings; an '=' inside the key would corrupt that format, so frp rejects it up front. Values may still contain '='; only the name is checked.","triggerScenarios":"tokenSource.exec.env entry like { name = \"A=B\", value = \"1\" }; building env from a raw string split on the first '=' incorrectly in code that generates frp config; copy-pasting a full KEY=VALUE pair into the name field.","commonSituations":"Users pasting \"FOO=bar\" into the name slot; config generators that split environment lines on the wrong delimiter; secrets wrappers that embed '=' in variable names.","solutions":["Put only the variable name in name and the remainder in value: { name = \"FOO\", value = \"bar\" }","If generating config programmatically, split each KEY=VALUE on the first '=' only (strings.SplitN(s, \"=\", 2))","Re-run frpc verify to confirm"],"exampleFix":"# before\n[[auth.tokenSource.exec.env]]\nname = \"FOO=bar\"\nvalue = \"\"\n\n# after\n[[auth.tokenSource.exec.env]]\nname = \"FOO\"\nvalue = \"bar\"","handlingStrategy":"validation","validationCode":"for _, kv := range vs.Exec.Env {\n    if strings.Contains(kv.Name, \"=\") {\n        return fmt.Errorf(\"env name %q must not contain '='\", kv.Name)\n    }\n}","typeGuard":"func isPlainEnvName(name string) bool {\n    return name != \"\" && !strings.ContainsAny(name, \"=\")\n}","tryCatchPattern":null,"preventionTips":["When converting KEY=VALUE strings to env entries, split on the first '=' only and put each half in the right field","Never paste a full KEY=VALUE pair into the name field of the config"],"tags":["frp","go","config","validation","exec","env"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}