{"record":{"id":"4a315aab4fdc206a","repo":"mudler/LocalAI","slug":"router-score-unknown-score-normalization-q-want","errorCode":null,"errorMessage":"router/score: unknown score_normalization %q (want %q or %q)","messagePattern":"router/score: unknown score_normalization %q \\(want %q or %q\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/services/routing/router/score.go","lineNumber":199,"sourceCode":"\tfor _, p := range policies {\n\t\tlabels = append(labels, p.Label)\n\t}\n\tif opts.ActivationThreshold <= 0 {\n\t\topts.ActivationThreshold = defaultActivationThreshold\n\t}\n\tif opts.StopToken == \"\" {\n\t\topts.StopToken = defaultStopToken\n\t}\n\tif opts.PromptRenderer == nil {\n\t\topts.PromptRenderer = chatMLRenderer\n\t}\n\tswitch opts.Normalization {\n\tcase \"\", ScoreNormalizationRaw:\n\t\topts.Normalization = ScoreNormalizationRaw\n\tcase ScoreNormalizationMean:\n\t\t// ok\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"router/score: unknown score_normalization %q (want %q or %q)\",\n\t\t\topts.Normalization, ScoreNormalizationRaw, ScoreNormalizationMean))\n\t}\n\tcandidates := make([]string, len(labels))\n\tfor i, l := range labels {\n\t\tcandidates[i] = buildCandidate(l, opts.StopToken)\n\t}\n\tsystemPrompt, err := renderSystemPrompt(opts.SystemPromptTemplate, policies)\n\tif err != nil {\n\t\t// Parse-time error here means the operator-supplied template\n\t\t// is malformed. Config-load validation (ModelConfig.Validate)\n\t\t// should have caught it earlier; reaching this is either a\n\t\t// direct programmatic construction or a validation gap.\n\t\tpanic(fmt.Sprintf(\"router/score: system_prompt_template: %v\", err))\n\t}\n\treturn &ScoreClassifier{\n\t\tscorer:              scorer,\n\t\tactivationThreshold: opts.ActivationThreshold,\n\t\tnormalization:       opts.Normalization,","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/mudler/LocalAI/blob/44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26/core/services/routing/router/score.go#L181-L217","documentation":"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.","triggerScenarios":"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.","commonSituations":"Operators inventing normalization names from intuition; docs/examples referencing a mode removed in a refactor; config values copied from a different classifier implementation.","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"],"exampleFix":"# before (yaml)\nclassifier:\n  type: score\n  score_normalization: average\n# after\nclassifier:\n  type: score\n  score_normalization: mean","handlingStrategy":"type-guard","validationCode":"switch cfg.Normalization {\ncase \"\", string(router.ScoreNormalizationRaw), string(router.ScoreNormalizationMean):\ndefault:\n    return fmt.Errorf(\"score_normalization must be raw or mean, got %q\", cfg.Normalization)\n}","typeGuard":"func validScoreNormalization(s string) bool { return s == \"\" || s == string(ScoreNormalizationRaw) || s == string(ScoreNormalizationMean) }","tryCatchPattern":null,"preventionTips":["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"],"tags":["router","config","enum","validation"],"backgroundTag":null,"analyzedSha":"44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26","analyzedAt":"2026-08-15T10:13:50.291Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}