fatedier/frp · error

no sources configured

Error message

no sources configured

What it means

Aggregator.Load (pkg/config/source/aggregator.go:76) refuses to load when getSourcesLocked returns zero sources. The aggregator merges a config file source and an optional store source; a zero-value Aggregator (struct created without NewAggregator) has both fields nil, hence no sources. NewAggregator itself always installs a config source, so in practice this error means the aggregator was constructed incorrectly rather than configured empty.

Source

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

func (a *Aggregator) getSourcesLocked() []Source {
	sources := make([]Source, 0, 2)
	if a.configSource != nil {
		sources = append(sources, a.configSource)
	}
	if a.storeSource != nil {
		sources = append(sources, a.storeSource)
	}
	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)

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Construct with source.NewAggregator(cfgSource) (a nil argument is fine — it creates a default ConfigSource)
  2. Or call SetStoreSource / ensure a config source is attached before Load
  3. Treat this error as a programming bug in the caller, not a runtime condition to retry
  4. Add a unit test asserting Load works right after NewAggregator

Example fix

// before
agg := &source.Aggregator{}
_, _, err := agg.Load() // no sources configured

// after
agg := source.NewAggregator(nil)
_, _, err := agg.Load()
Defensive patterns

Strategy: validation

Validate before calling

// Never use a zero-value aggregator
var agg *source.Aggregator // unsafe if Load() called

agg := source.NewAggregator(nil) // always has >= 1 source

Try / catch

proxies, visitors, err := agg.Load()
if err != nil && strings.Contains(err.Error(), "no sources configured") {
    // construction bug — fix caller, do not retry
    return fmt.Errorf("aggregator misuse: construct with NewAggregator: %w", err)
}

Prevention

When it happens

Trigger: Using `var agg source.Aggregator` or `&source.Aggregator{}` directly and calling Load(); NewAggregator always returns an aggregator with a non-nil configSource, so library consumers bypassing the constructor are the real trigger.

Common situations: Embedding frp's config source package in another tool and instantiating the struct literal instead of NewAggregator; refactoring that drops the constructor call.

Related errors


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