fatedier/frp · error

failed to parse JSON: %w

Error message

failed to parse JSON: %w

What it means

loadFromFileUnlocked read the store file successfully but its contents are not valid JSON, so jsonx.Unmarshal into the raw {proxies, visitors} envelope failed. The store file must be a JSON object whose proxies/visitors members are arrays; any syntax error anywhere in the file produces this error.

Source

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

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
	}

	type rawStoreData struct {
		Proxies  []jsonx.RawMessage `json:"proxies,omitempty"`
		Visitors []jsonx.RawMessage `json:"visitors,omitempty"`
	}
	stored := rawStoreData{}
	if err := jsonx.Unmarshal(data, &stored); err != nil {
		return fmt.Errorf("failed to parse JSON: %w", err)
	}

	s.proxies = make(map[string]v1.ProxyConfigurer)
	s.visitors = make(map[string]v1.VisitorConfigurer)

	for i, proxyData := range stored.Proxies {
		proxyCfg, err := v1.DecodeProxyConfigurerJSON(proxyData, v1.DecodeOptions{
			DisallowUnknownFields: false,
		})
		if err != nil {
			return fmt.Errorf("failed to decode proxy at index %d: %w", i, err)
		}
		name := proxyCfg.GetBaseConfig().Name
		if name == "" {
			return fmt.Errorf("proxy name cannot be empty")
		}
		s.proxies[name] = proxyCfg
	}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Validate the file offline: jq . <path> — jq pinpoints the line/column of the syntax error
  2. Fix the reported syntax issue (trailing comma, missing quote, comment) and retry NewStoreSource
  3. If the file is unusable, restore it from backup or VCS history (git checkout -- store.json)
  4. As a last resort, move the file aside (mv store.json store.json.bak) to start with an empty store, then re-add proxies/visitors through the API

Example fix

// before: file content
{ "proxies": [ {"type":"tcp","name":"web"}, ], }

// after: valid JSON
{ "proxies": [ {"type":"tcp","name":"web"} ] }
Defensive patterns

Strategy: validation

Validate before calling

func validateStoreJSON(path string) error {
	data, err := os.ReadFile(path)
	if errors.Is(err, fs.ErrNotExist) {
		return nil
	}
	if err != nil {
		return err
	}
	if !json.Valid(data) {
		return fmt.Errorf("store file %s is not valid JSON", path)
	}
	return nil
}

Try / catch

src, err := source.NewStoreSource(cfg)
if err != nil && strings.Contains(err.Error(), "failed to parse JSON") {
	// quarantine the bad file and start empty (accepting data loss) or restore from backup
	_ = os.Rename(cfg.Path, cfg.Path+".corrupt")
}

Prevention

When it happens

Trigger: The store file was hand-edited and contains a trailing comma, unquoted key, or comment; the file was truncated by an external process (this library itself writes atomically via temp-file + rename, so self-inflicted truncation is unlikely); an empty (0-byte) file also fails JSON parsing; a file with a UTF-8 BOM or C2 A0 characters pasted from a rich-text editor.

Common situations: Manually tweaking the generated store JSON instead of using the API; a git merge conflict resolved badly on a committed store file; a partially synced/copied file between hosts; running an old frp version that wrote a non-JSON format (e.g. TOML/INI) at the same path.

Understand the failure class

Related errors


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