mudler/LocalAI · error
router/score: unknown score_normalization %q (want %q or %q)
Error message
router/score: unknown score_normalization %q (want %q or %q)
What it means
Construction-time panic in NewScoreClassifier: opts.Normalization is neither empty/raw nor ScoreNormalizationMean. Only two normalization modes exist for turning per-label logprob scores into comparable numbers — raw (no transform) and mean (average over label tokens) — anything else (e.g. 'sum', 'softmax', 'average') is rejected.
Source
Thrown at core/services/routing/router/score.go:199
for _, p := range policies {
labels = append(labels, p.Label)
}
if opts.ActivationThreshold <= 0 {
opts.ActivationThreshold = defaultActivationThreshold
}
if opts.StopToken == "" {
opts.StopToken = defaultStopToken
}
if opts.PromptRenderer == nil {
opts.PromptRenderer = chatMLRenderer
}
switch opts.Normalization {
case "", ScoreNormalizationRaw:
opts.Normalization = ScoreNormalizationRaw
case ScoreNormalizationMean:
// ok
default:
panic(fmt.Sprintf("router/score: unknown score_normalization %q (want %q or %q)",
opts.Normalization, ScoreNormalizationRaw, ScoreNormalizationMean))
}
candidates := make([]string, len(labels))
for i, l := range labels {
candidates[i] = buildCandidate(l, opts.StopToken)
}
systemPrompt, err := renderSystemPrompt(opts.SystemPromptTemplate, policies)
if err != nil {
// Parse-time error here means the operator-supplied template
// is malformed. Config-load validation (ModelConfig.Validate)
// should have caught it earlier; reaching this is either a
// direct programmatic construction or a validation gap.
panic(fmt.Sprintf("router/score: system_prompt_template: %v", err))
}
return &ScoreClassifier{
scorer: scorer,
activationThreshold: opts.ActivationThreshold,
normalization: opts.Normalization,View on GitHub (pinned to 44413a9d06)
Solutions
- Use 'raw' or 'mean' (the exact ScoreNormalizationRaw / ScoreNormalizationMean values)
- Leave score_normalization unset to get the raw default
- If a new mode is genuinely needed, add it as a ScoreNormalization* constant in score.go rather than passing arbitrary strings
Example fix
# before (yaml) classifier: type: score score_normalization: average # after classifier: type: score score_normalization: mean
Defensive patterns
Strategy: type-guard
Validate before calling
switch cfg.Normalization {
case "", string(router.ScoreNormalizationRaw), string(router.ScoreNormalizationMean):
default:
return fmt.Errorf("score_normalization must be raw or mean, got %q", cfg.Normalization)
} Type guard
func validScoreNormalization(s string) bool { return s == "" || s == string(ScoreNormalizationRaw) || s == string(ScoreNormalizationMean) } Prevention
- Restrict config UIs/editors to a dropdown of the two valid modes
- Leave the key unset unless normalization is actually needed
- Grep release notes when upgrading router config schemas
When it happens
Trigger: Setting score_normalization: 'sum' or 'average' in the router classifier options; passing a ScoreClassifierOptions with Normalization set from user input without whitelisting; case/format variants like 'Mean' not matching the constants.
Common situations: Operators inventing normalization names from intuition; docs/examples referencing a mode removed in a refactor; config values copied from a different classifier implementation.
Related errors
- router/rerank: policy has empty label
- router/rerank: policy %q has no description
- router/score: policy has empty label
- router/score: policy %q has no description
- router/score: system_prompt_template: %v
AI-assisted analysis of mudler/LocalAI@44413a9d06 (2026-08-15).
Data as JSON: /api/errors/4a315aab4fdc206a.
Report an issue: GitHub.