XTLS/Xray-core · error

not a StrategyLeastLoadConfig

Error message

not a StrategyLeastLoadConfig

What it means

For a 'leastload' balancing rule, StrategySettings must decode to *StrategyLeastLoadConfig. If the settings message is absent-but-typed differently or the embedded instance is another config type, the assertion fails and the balancer build aborts.

Source

Thrown at app/router/config.go:148

			strategy:    &LeastPingStrategy{},
			fallbackTag: br.FallbackTag,
			ohm:         ohm,
		}, nil
	case "roundrobin":
		return &Balancer{
			selectors:   br.OutboundSelector,
			strategy:    &RoundRobinStrategy{FallbackTag: br.FallbackTag},
			fallbackTag: br.FallbackTag,
			ohm:         ohm,
		}, nil
	case "leastload":
		i, err := br.StrategySettings.GetInstance()
		if err != nil {
			return nil, err
		}
		s, ok := i.(*StrategyLeastLoadConfig)
		if !ok {
			return nil, errors.New("not a StrategyLeastLoadConfig").AtError()
		}
		leastLoadStrategy := NewLeastLoadStrategy(s)
		return &Balancer{
			selectors:   br.OutboundSelector,
			ohm:         ohm,
			fallbackTag: br.FallbackTag,
			strategy:    leastLoadStrategy,
		}, nil
	case "random":
		fallthrough
	case "":
		return &Balancer{
			selectors:   br.OutboundSelector,
			ohm:         ohm,
			fallbackTag: br.FallbackTag,
			strategy:    &RandomStrategy{FallbackTag: br.FallbackTag},
		}, nil
	default:

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Use the correct settings type for leastload: {"type": "leastload", "settings": {...}} via StrategyLeastLoadConfig fields (costs, baselines, expected status counts)
  2. Remove strategySettings entirely to use defaults if customization is not needed
  3. Regenerate the config with a current schema-aware tool

Example fix

// before
"strategy": {
  "type": "leastload",
  "settings": { "type": "pingconfig" } // wrong settings type
}

// after
"strategy": {
  "type": "leastload"
} // defaults, or supply leastload-specific settings
Defensive patterns

Strategy: validation

Validate before calling

if strings.EqualFold(br.Strategy, "leastload") && br.StrategySettings != nil {
    i, _ := br.StrategySettings.GetInstance()
    if _, ok := i.(*router.StrategyLeastLoadConfig); !ok {
        return fmt.Errorf("strategySettings must be StrategyLeastLoadConfig for leastload")
    }
}

Type guard

func isLeastLoadConfig(i interface{}) bool {
    _, ok := i.(*router.StrategyLeastLoadConfig)
    return ok
}

Prevention

When it happens

Trigger: Declaring strategy 'leastload' with a strategySettings block whose serialized type is not StrategyLeastLoadConfig (e.g. pasted leastping settings or a random strategy object).

Common situations: Copying strategy blocks between rules of different strategy types; JSON configs hand-migrated between versions where settings type names changed.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/98aa4bf889aa6bb0. Report an issue: GitHub.