fatedier/frp · error · configmgmt.ErrApplyConfig

apply config failed: proxy

Error message

apply config failed: proxy %q not found in store after mutation

What it means

After a proxy mutation and successful reload, withStoreProxyMutationAndReload re-reads the proxy from the store; if GetProxy returns nil it fails with ErrApplyConfig. This is an internal consistency check — the proxy that was just written is no longer readable, meaning the store write did not stick or the store was mutated concurrently.

Solutions

  1. Retry the operation once (likely transient race)
  2. Serialize store mutations with a lock in your automation so only one writer runs at a time
  3. Verify only one frpc process uses the store path
  4. Check store file health/permissions on disk
Defensive patterns

Strategy: retry

Prevention

When it happens

Trigger: Rare race: another writer (a second admin API call, or an external process touching the store file at common.store.path) removes/renames the proxy between the mutation and the read-back; or the store backend silently dropped the write.

Common situations: Concurrent API calls from multiple automation tools; store file on flaky storage; two frpc instances pointed at the same store file.

Related errors


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

Appendix: source

Thrown at client/config_manager.go:411

) (v1.ProxyConfigurer, error) {
	m.svr.reloadMu.Lock()
	defer m.svr.reloadMu.Unlock()

	storeSource := m.svr.storeSource
	if storeSource == nil {
		return nil, fmt.Errorf("%w: store API is disabled", configmgmt.ErrStoreDisabled)
	}

	if err := fn(storeSource); err != nil {
		return nil, err
	}
	if err := m.svr.reloadConfigFromSourcesLocked(); err != nil {
		return nil, fmt.Errorf("%w: failed to apply config: %v", configmgmt.ErrApplyConfig, err)
	}

	persisted := storeSource.GetProxy(name)
	if persisted == nil {
		return nil, fmt.Errorf("%w: proxy %q not found in store after mutation", configmgmt.ErrApplyConfig, name)
	}
	return persisted.Clone(), nil
}

func (m *serviceConfigManager) withStoreVisitorMutationAndReload(
	name string,
	fn func(storeSource *source.StoreSource) error,
) (v1.VisitorConfigurer, error) {
	m.svr.reloadMu.Lock()
	defer m.svr.reloadMu.Unlock()

	storeSource := m.svr.storeSource
	if storeSource == nil {
		return nil, fmt.Errorf("%w: store API is disabled", configmgmt.ErrStoreDisabled)
	}

	if err := fn(storeSource); err != nil {
		return nil, err

View on GitHub (pinned to 6c8a8d0a97)