mudler/LocalAI · error

router/score: at least one policy is required

Error message

router/score: at least one policy is required

What it means

Construction-time panic in NewScoreClassifier (token-based score classifier): the policies slice is empty. Like the rerank classifier, score policies define the labels candidates are built from via buildCandidate(label, stopToken); with none there is nothing to score.

Source

Thrown at core/services/routing/router/score.go:167

	// stablePrefix is the rendered-prompt prefix shared by every probe:
	// the chat template's preamble plus the option-list system prompt,
	// up to where the per-turn text begins. Computed once (the byte-wise
	// common prefix of two synthetic probes) and sent with each Score
	// call as a state-reuse boundary hint — on backends whose models
	// cannot rewind state (hybrid/recurrent), a snapshot at this
	// boundary is what keeps repeat scoring at probe-size cost instead
	// of a full option-list re-prefill.
	stablePrefixOnce sync.Once
	stablePrefix     string
}

// NewScoreClassifier panics on caller errors at construction (empty
// policies, missing description, nil scorer) — same rationale as the
// other classifiers. See ScoreClassifierOptions for the optional
// knobs and their zero-value defaults.
func NewScoreClassifier(policies []ScorePolicy, scorer backend.Scorer, opts ScoreClassifierOptions) *ScoreClassifier {
	if len(policies) == 0 {
		panic("router/score: at least one policy is required")
	}
	if scorer == nil {
		panic("router/score: scorer is required (configure router.classifier_model)")
	}
	for _, p := range policies {
		if p.Label == "" {
			panic("router/score: policy has empty label")
		}
		if p.Description == "" {
			panic(fmt.Sprintf("router/score: policy %q has no description", p.Label))
		}
	}
	labels := make([]string, 0, len(policies))
	for _, p := range policies {
		labels = append(labels, p.Label)
	}
	if opts.ActivationThreshold <= 0 {
		opts.ActivationThreshold = defaultActivationThreshold

View on GitHub (pinned to 44413a9d06)

Solutions

  1. Add one or more policies (label + description) under the score classifier config
  2. Verify YAML nesting places policies inside the classifier block
  3. In Go callers, skip classifier construction when policies is empty rather than calling the constructor

Example fix

# before (yaml)
router:
  classifier:
    type: score
    policies: []
# after
router:
  classifier:
    type: score
    policies:
      - label: chat
        description: "general conversation"
Defensive patterns

Strategy: validation

Validate before calling

if len(cfg.Router.Classifier.Policies) == 0 {
    return fmt.Errorf("router: score classifier requires at least one policy")
}

Type guard

func hasPolicies(p []ScorePolicy) bool { return len(p) > 0 }

Try / catch

defer func() { if r := recover(); r != nil { log.Printf("router init failed: %v", r) } }()

Prevention

When it happens

Trigger: Configuring a score-type router classifier without a policies list; passing an empty slice programmatically; a config loader filtering out all policies (e.g. due to per-entry validation failures) before construction.

Common situations: Switching classifier type from rerank to score and forgetting to carry policies over; indentation errors detaching the policies block; scaffolding config from a template with the policies section commented out.

Related errors


AI-assisted analysis of mudler/LocalAI@44413a9d06 (2026-08-15). Data as JSON: /api/errors/61dcece42ce738df. Report an issue: GitHub.