fatedier/frp · error

failed to load existing data: %w

Error message

failed to load existing data: %w

What it means

Returned by NewStoreSource when the store file at cfg.Path exists but cannot be read for any reason other than 'file does not exist'. The constructor intentionally treats a missing file as an empty store, so this error means the path exists yet reading it failed (permissions, path is a directory, I/O error). Construction is aborted and no StoreSource is returned.

Source

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

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()
	defer s.mu.Unlock()
	return s.loadFromFileUnlocked()
}

func (s *StoreSource) loadFromFileUnlocked() error {
	data, err := os.ReadFile(s.config.Path)
	if err != nil {
		return err
	}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Check the path and its permissions: ls -la <path> and confirm it is a regular file readable by the account running the process
  2. Fix ownership/permissions: chown <user> <path> or chmod 644 <path> (or run the process as the owning user)
  3. If Path points to a directory, change it to a file path such as /var/lib/frpc/store.json
  4. If the file is stale and disposable, move or delete it so NewStoreSource starts with an empty store (this discards stored proxies/visitors)
  5. On network filesystems, verify the mount is healthy (mount | grep <dir>, dmesg) before restarting

Example fix

# before: store file created by root, frpc runs as service user
Path: /var/lib/frpc/store.json   # -rw------- root root

# after
sudo chown frpc:frpc /var/lib/frpc/store.json
sudo chmod 600 /var/lib/frpc/store.json
Defensive patterns

Strategy: try-catch

Validate before calling

func checkStorePathReadable(path string) error {
	fi, err := os.Stat(path)
	if errors.Is(err, fs.ErrNotExist) {
		return nil // missing file is fine: NewStoreSource treats it as empty store
	}
	if err != nil {
		return err
	}
	if fi.IsDir() {
		return fmt.Errorf("%s is a directory, expected a file", path)
	}
	f, err := os.Open(path)
	if err != nil {
		return fmt.Errorf("store file not readable: %w", err)
	}
	f.Close()
	return nil
}

Try / catch

src, err := source.NewStoreSource(source.StoreSourceConfig{Path: p})
if err != nil {
	if os.IsPermission(err) || errors.Is(err, fs.ErrPermission) {
		// fix ownership or run as owning user, then exit with actionable message
		log.Fatalf("no read access to store file %s: chown/chmod it (current uid=%d)", p, os.Getuid())
	}
	log.Fatalf("open store: %v", err)
}

Prevention

When it happens

Trigger: Calling NewStoreSource(StoreSourceConfig{Path: p}) where p is a directory, a file without read permission for the current user (e.g. mode 0600 owned by root while frpc runs as nobody), or a file on a failed/unmounted volume. The os.IsNotExist check in NewStoreSource only exempts ENOENT; every other error from loadFromFile lands here.

Common situations: Running the process once as root (or via sudo) so the store file becomes root-owned, then running it as a service user; pointing Path at a directory like /etc/frp instead of a file; an NFS/FUSE mount that dropped and returns EIO on read; SELinux denying access.

Related errors


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