{"record":{"id":"43ff683f64aa1a79","repo":"fatedier/frp","slug":"exec-env-name-cannot-be-empty","errorCode":null,"errorMessage":"exec env name cannot be empty","messagePattern":"exec env name cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/v1/value_source.go","lineNumber":128,"sourceCode":"\t}\n\n\t// Trim whitespace, which is important for file-based tokens\n\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 {","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/v1/value_source.go#L110-L146","documentation":"Thrown by ExecSource.Validate() while iterating e.Env when an env entry has an empty Name. Env entries are name/value pairs appended to the subprocess environment by ExecSource.Resolve; a nameless entry would be meaningless or misinterpreted by exec.Cmd.Env.","triggerScenarios":"tokenSource.exec.env = [{ value = \"x\" }] with name omitted; Go literal v1.EnvEntry{Name: \"\", Value: \"v\"}; TOML array-of-tables entry that lost its name key through templating.","commonSituations":"Templated env lists (Helm values, envsubst) where the variable holding the env name is empty; hand-edited env arrays; YAML-to-TOML conversion dropping empty-looking keys.","solutions":["Give every entry in tokenSource.exec.env a non-empty name, e.g. { name = \"ROLE\", value = \"frp\" }","Remove entries whose name could not be resolved at deploy time instead of shipping them empty","Re-run frpc verify -c ./frpc.toml to confirm"],"exampleFix":"# before\n[[auth.tokenSource.exec.env]]\nvalue = \"frp\"\n\n# after\n[[auth.tokenSource.exec.env]]\nname = \"ROLE\"\nvalue = \"frp\"","handlingStrategy":"validation","validationCode":"for _, e := range vs.Exec.Env {\n    if strings.TrimSpace(e.Name) == \"\" {\n        return fmt.Errorf(\"exec env entry with empty name\")\n    }\n}","typeGuard":"func validEnvNames(e *v1.ExecSource) bool {\n    for _, kv := range e.Env {\n        if kv.Name == \"\" {\n            return false\n        }\n    }\n    return true\n}","tryCatchPattern":null,"preventionTips":["When generating env lists from templates, drop entries whose name variable is unset rather than emitting them","Lint generated frp configs with frpc verify in CI"],"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"}