{"record":{"id":"bff0f08a56312b93","repo":"fatedier/frp","slug":"file-path-cannot-be-empty","errorCode":null,"errorMessage":"file path cannot be empty","messagePattern":"file path cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/v1/value_source.go","lineNumber":96,"sourceCode":"\n\tswitch v.Type {\n\tcase \"file\":\n\t\treturn v.File.Resolve(ctx)\n\tcase \"exec\":\n\t\treturn v.Exec.Resolve(ctx)\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unsupported value source type: %s\", v.Type)\n\t}\n}\n\n// Validate validates the FileSource configuration.\nfunc (f *FileSource) Validate() error {\n\tif f == nil {\n\t\treturn errors.New(\"fileSource cannot be nil\")\n\t}\n\n\tif f.Path == \"\" {\n\t\treturn errors.New(\"file path cannot be empty\")\n\t}\n\treturn nil\n}\n\n// Resolve reads and returns the content from the specified file.\nfunc (f *FileSource) Resolve(_ context.Context) (string, error) {\n\tif err := f.Validate(); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tcontent, err := os.ReadFile(f.Path)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to read file %s: %v\", f.Path, err)\n\t}\n\n\t// Trim whitespace, which is important for file-based tokens\n\treturn strings.TrimSpace(string(content)), nil\n}","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/v1/value_source.go#L78-L114","documentation":"Thrown by FileSource.Validate() when a ValueSource of type \"file\" has an empty Path. FileSource.Resolve reads the file contents (trimmed) as the resolved value, typically an auth token, so an empty path is rejected before any os.ReadFile attempt.","triggerScenarios":"tokenSource.type = \"file\" with tokenSource.file.path omitted or set to \"\"; Go literal v1.ValueSource{Type: \"file\", File: &v1.FileSource{}} with Path unset; templated config where the path variable expands to empty.","commonSituations":"Copying a file-based token example but leaving the path placeholder; secrets-in-file setups (e.g. Docker/Kubernetes secret mounted paths) where the mount path was changed but the config was not; relative paths that were intended but never filled in.","solutions":["Set tokenSource.file.path to the absolute path of the token file, e.g. path = \"/etc/frp/token\"","Verify the file exists and frpc/frps can read it (check permissions) after fixing the path","Run frpc verify -c ./frpc.toml to confirm the config parses and validates","If the token is produced by a command, switch to type = \"exec\" with a command instead"],"exampleFix":"# before\n[auth]\ntokenSource.type = \"file\"\n[auth.tokenSource.file]\n\n# after\n[auth]\ntokenSource.type = \"file\"\n[auth.tokenSource.file]\npath = \"/etc/frp/token\"","handlingStrategy":"validation","validationCode":"if vs := cfg.Auth.TokenSource; vs != nil && vs.Type == \"file\" && vs.File != nil {\n    if strings.TrimSpace(vs.File.Path) == \"\" {\n        return fmt.Errorf(\"tokenSource.file.path is empty\")\n    }\n}","typeGuard":"func hasFileSource(vs *v1.ValueSource) bool {\n    return vs != nil && vs.Type == \"file\" && vs.File != nil && vs.File.Path != \"\"\n}","tryCatchPattern":null,"preventionTips":["Use absolute paths for token files and check existence with a pre-start probe","In containers, verify the secret mount path matches tokenSource.file.path before launching frpc"],"tags":["frp","go","config","validation","auth"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}