fatedier/frp · error

load source: %w

Error message

load source: %w

What it means

The aggregator source iterates every registered config source (file, store, etc.) and calls src.Load(); the first source that fails aborts aggregation with 'load source: %w'. The %w wrapping preserves the underlying error for errors.Is/As inspection, so the real cause (file parse failure, store read failure) stays chainable.

Source

Thrown at pkg/config/source/aggregator.go:85

	return sources
}

func (a *Aggregator) Load() ([]v1.ProxyConfigurer, []v1.VisitorConfigurer, error) {
	a.mu.RLock()
	entries := a.getSourcesLocked()
	a.mu.RUnlock()

	if len(entries) == 0 {
		return nil, nil, errors.New("no sources configured")
	}

	proxyMap := make(map[string]v1.ProxyConfigurer)
	visitorMap := make(map[string]v1.VisitorConfigurer)

	for _, src := range entries {
		proxies, visitors, err := src.Load()
		if err != nil {
			return nil, nil, fmt.Errorf("load source: %w", err)
		}
		for _, p := range proxies {
			proxyMap[p.GetBaseConfig().Name] = p
		}
		for _, v := range visitors {
			visitorMap[v.GetBaseConfig().Name] = v
		}
	}
	proxies, visitors := a.mapsToSortedSlices(proxyMap, visitorMap)
	return proxies, visitors, nil
}

func (a *Aggregator) mapsToSortedSlices(
	proxyMap map[string]v1.ProxyConfigurer,
	visitorMap map[string]v1.VisitorConfigurer,
) ([]v1.ProxyConfigurer, []v1.VisitorConfigurer) {
	proxies := slices.SortedFunc(maps.Values(proxyMap), func(x, y v1.ProxyConfigurer) int {
		return cmp.Compare(x.GetBaseConfig().Name, y.GetBaseConfig().Name)

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Inspect the wrapped error chain (errors.Unwrap / string suffix) to find which source and why
  2. Fix that source's underlying file/content
  3. For watched sources, write changes atomically (write temp file + rename) so the watcher never reads a half-written config

Example fix

# before: echo 'bad toml' >> /etc/frp/frpc.toml while running
# after: write /etc/frp/frpc.toml.tmp fully, then mv /etc/frp/frpc.toml.tmp /etc/frp/frpc.toml
Defensive patterns

Strategy: try-catch

Validate before calling

// probe each source before wiring the aggregator
for _, src := range sources { if _, _, err := src.Load(); err != nil { return fmt.Errorf("source %T unhealthy: %w", src, err) } }

Try / catch

if _, _, err := aggregator.Load(); err != nil {
    var cause error = errors.Unwrap(err) // 'load source: %w' keeps chain
    for cause != nil && errors.Unwrap(cause) != nil { cause = errors.Unwrap(cause) }
    // log full chain; identify failing source type
}

Prevention

When it happens

Trigger: Any configured source failing to load: a file source watching a config that becomes invalid, or a store source whose JSON file is corrupted. Sources load in registration order, so the first failure wins.

Common situations: frpc deployments with multiple config sources (static file + dynamic store); editing the live-watched config file into a temporarily invalid state; a corrupted store JSON after disk issues.

Related errors


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