{"record":{"id":"81fff507726c6927","repo":"fatedier/frp","slug":"path-is-required","errorCode":null,"errorMessage":"path is required","messagePattern":"path is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/source/store.go","lineNumber":53,"sourceCode":"\ntype StoreSource struct {\n\tbaseSource\n\tconfig StoreSourceConfig\n}\n\nvar (\n\tErrAlreadyExists = errors.New(\"already exists\")\n\tErrNotFound      = errors.New(\"not found\")\n)\n\nconst (\n\tstoreKindProxy   = \"proxy\"\n\tstoreKindVisitor = \"visitor\"\n)\n\nfunc NewStoreSource(cfg StoreSourceConfig) (*StoreSource, error) {\n\tif cfg.Path == \"\" {\n\t\treturn nil, fmt.Errorf(\"path is required\")\n\t}\n\n\ts := &StoreSource{\n\t\tbaseSource: newBaseSource(),\n\t\tconfig:     cfg,\n\t}\n\n\tif err := s.loadFromFile(); err != nil {\n\t\tif !os.IsNotExist(err) {\n\t\t\treturn nil, fmt.Errorf(\"failed to load existing data: %w\", err)\n\t\t}\n\t}\n\n\treturn s, nil\n}\n\nfunc (s *StoreSource) loadFromFile() error {\n\ts.mu.Lock()","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/source/store.go#L35-L71","documentation":"NewStoreSource constructs the dynamic store-backed config source (used for API-managed proxies/visitors persisted to a JSON file). StoreSourceConfig has a single Path field; constructing with an empty Path returns 'path is required' before anything is read. A non-existent file is fine (it is created lazily), but no path at all is a programming error.","triggerScenarios":"Calling source.NewStoreSource(source.StoreSourceConfig{Path: \"\"}) — e.g. the path flag was not parsed, was typo'd, or the config struct was zero-valued when wiring the aggregator.","commonSituations":"Embedding frp as a library and wiring a store source; config-flag parsing that silently drops the store path (typo'd flag name) leading to an empty string at construction.","solutions":["Set cfg.Path to a writable file location (e.g. /var/lib/frp/store.json)","Ensure the frpc process user can create/write that path (parent dir exists, correct ownership)","If constructing programmatically, fail fast on empty Path before calling NewStoreSource"],"exampleFix":"// before\nsrc, err := source.NewStoreSource(source.StoreSourceConfig{})\n\n// after\nsrc, err := source.NewStoreSource(source.StoreSourceConfig{Path: \"/var/lib/frp/store.json\"})","handlingStrategy":"validation","validationCode":"if cfg.Path == \"\" { return nil, errors.New(\"StoreSourceConfig.Path must be set (e.g. /var/lib/frp/store.json)\") }\nif dir := filepath.Dir(cfg.Path); dir != \"\" { if _, err := os.Stat(dir); err != nil { os.MkdirAll(dir, 0o755) } }","typeGuard":"func hasStorePath(cfg source.StoreSourceConfig) bool { return strings.TrimSpace(cfg.Path) != \"\" }","tryCatchPattern":"src, err := source.NewStoreSource(cfg)\nif err != nil {\n    if err.Error() == \"path is required\" { /* fill cfg.Path from flag/env with a sane default and retry construction */ }\n    return err\n}","preventionTips":["Default the store path in your app's flag parsing (e.g. --store-path with default)","Fail fast on empty path at CLI parsing time","Ensure the store directory is writable by the service user"],"tags":["config","store","validation","api","frpc"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}