fatedier/frp · error · ErrApplyConfig

apply config failed: %v

Error message

apply config failed: %v

What it means

Returned by serviceConfigManager.ReloadFromFile when svr.UpdateConfigSource fails while applying the freshly loaded and validated config to the running frpc service — i.e. parsing and validation succeeded but the live swap-in failed. It wraps configmgmt.ErrApplyConfig to distinguish it from ErrInvalidArgument cases.

Source

Thrown at client/config_manager.go:49

	result, err := config.LoadClientConfigResult(m.svr.configFilePath, strict)
	if err != nil {
		return fmt.Errorf("%w: %v", configmgmt.ErrInvalidArgument, err)
	}

	proxyCfgsForValidation, visitorCfgsForValidation := config.FilterClientConfigurers(
		result.Common,
		result.Proxies,
		result.Visitors,
	)
	proxyCfgsForValidation = config.CompleteProxyConfigurers(proxyCfgsForValidation)
	visitorCfgsForValidation = config.CompleteVisitorConfigurers(visitorCfgsForValidation)

	if _, err := validation.ValidateAllClientConfig(result.Common, proxyCfgsForValidation, visitorCfgsForValidation, m.svr.unsafeFeatures); err != nil {
		return fmt.Errorf("%w: %v", configmgmt.ErrInvalidArgument, err)
	}

	if err := m.svr.UpdateConfigSource(result.Common, result.Proxies, result.Visitors); err != nil {
		return fmt.Errorf("%w: %v", configmgmt.ErrApplyConfig, err)
	}

	log.Infof("success reload conf")
	return nil
}

func (m *serviceConfigManager) ReadConfigFile() (string, error) {
	if m.svr.configFilePath == "" {
		return "", fmt.Errorf("%w: frpc has no config file path", configmgmt.ErrInvalidArgument)
	}

	content, err := os.ReadFile(m.svr.configFilePath)
	if err != nil {
		return "", fmt.Errorf("%w: %v", configmgmt.ErrInvalidArgument, err)
	}
	return string(content), nil
}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Check the %v detail and frpc logs for which proxy/visitor failed to apply; fix that item (free the local port, correct bindAddr)
  2. If reload state is wedged, do a full restart of frpc with the new config instead of hot reload
  3. Verify local ports for visitors/proxies are free on the frpc host (ss -ltnp)
  4. Keep hot reloads to config-only changes; restart for changes that rebind listeners

Example fix

# before — visitor bindPort 6000 already used locally
[[visitors]]
name = "stcp-v"
type = "stcp"
bindAddr = "127.0.0.1"
bindPort = 6000

# after
bindPort = 6100
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-check local bind ports for visitors/proxies before reload
for _, v := range visitorCfgs {
    ln, err := net.Listen("tcp", net.JoinHostPort(v.BindAddr, strconv.Itoa(v.BindPort)))
    if err != nil {
        return fmt.Errorf("visitor %s cannot bind %s:%d: %v", v.Name, v.BindAddr, v.BindPort, err)
    }
    ln.Close()
}

Type guard

func isApplyConfig(err error) bool {
    return errors.Is(err, configmgmt.ErrApplyConfig)
}

Try / catch

if err := configManager.ReloadFromFile(true); err != nil {
    if errors.Is(err, configmgmt.ErrApplyConfig) {
        // config is valid but could not be applied live: fall back to a full frpc restart with the new file
    }
}

Prevention

When it happens

Trigger: Reloading a config whose proxies/visitors cannot be brought up at runtime (e.g. binding a local port already in use on the frpc host, or a listener type the running service cannot restart); internal state-transition failures inside the client service during hot reload.

Common situations: Hot-reload after editing bindAddr/bindPort of visitors to a port occupied by another process on the frpc machine; reloading while the previous config's proxies are mid-teardown; resource limits preventing new listeners.

Related errors


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