fatedier/frp · error

path is required

Error message

path is required

What it means

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.

Source

Thrown at pkg/config/source/store.go:53

type StoreSource struct {
	baseSource
	config StoreSourceConfig
}

var (
	ErrAlreadyExists = errors.New("already exists")
	ErrNotFound      = errors.New("not found")
)

const (
	storeKindProxy   = "proxy"
	storeKindVisitor = "visitor"
)

func NewStoreSource(cfg StoreSourceConfig) (*StoreSource, error) {
	if cfg.Path == "" {
		return nil, fmt.Errorf("path is required")
	}

	s := &StoreSource{
		baseSource: newBaseSource(),
		config:     cfg,
	}

	if err := s.loadFromFile(); err != nil {
		if !os.IsNotExist(err) {
			return nil, fmt.Errorf("failed to load existing data: %w", err)
		}
	}

	return s, nil
}

func (s *StoreSource) loadFromFile() error {
	s.mu.Lock()

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Set cfg.Path to a writable file location (e.g. /var/lib/frp/store.json)
  2. Ensure the frpc process user can create/write that path (parent dir exists, correct ownership)
  3. If constructing programmatically, fail fast on empty Path before calling NewStoreSource

Example fix

// before
src, err := source.NewStoreSource(source.StoreSourceConfig{})

// after
src, err := source.NewStoreSource(source.StoreSourceConfig{Path: "/var/lib/frp/store.json"})
Defensive patterns

Strategy: validation

Validate before calling

if cfg.Path == "" { return nil, errors.New("StoreSourceConfig.Path must be set (e.g. /var/lib/frp/store.json)") }
if dir := filepath.Dir(cfg.Path); dir != "" { if _, err := os.Stat(dir); err != nil { os.MkdirAll(dir, 0o755) } }

Type guard

func hasStorePath(cfg source.StoreSourceConfig) bool { return strings.TrimSpace(cfg.Path) != "" }

Try / catch

src, err := source.NewStoreSource(cfg)
if err != nil {
    if err.Error() == "path is required" { /* fill cfg.Path from flag/env with a sane default and retry construction */ }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/81fff507726c6927. Report an issue: GitHub.