{"record":{"id":"61dcece42ce738df","repo":"mudler/LocalAI","slug":"router-score-at-least-one-policy-is-required","errorCode":null,"errorMessage":"router/score: at least one policy is required","messagePattern":"router/score: at least one policy is required","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/services/routing/router/score.go","lineNumber":167,"sourceCode":"\t// stablePrefix is the rendered-prompt prefix shared by every probe:\n\t// the chat template's preamble plus the option-list system prompt,\n\t// up to where the per-turn text begins. Computed once (the byte-wise\n\t// common prefix of two synthetic probes) and sent with each Score\n\t// call as a state-reuse boundary hint — on backends whose models\n\t// cannot rewind state (hybrid/recurrent), a snapshot at this\n\t// boundary is what keeps repeat scoring at probe-size cost instead\n\t// of a full option-list re-prefill.\n\tstablePrefixOnce sync.Once\n\tstablePrefix     string\n}\n\n// NewScoreClassifier panics on caller errors at construction (empty\n// policies, missing description, nil scorer) — same rationale as the\n// other classifiers. See ScoreClassifierOptions for the optional\n// knobs and their zero-value defaults.\nfunc NewScoreClassifier(policies []ScorePolicy, scorer backend.Scorer, opts ScoreClassifierOptions) *ScoreClassifier {\n\tif len(policies) == 0 {\n\t\tpanic(\"router/score: at least one policy is required\")\n\t}\n\tif scorer == nil {\n\t\tpanic(\"router/score: scorer is required (configure router.classifier_model)\")\n\t}\n\tfor _, p := range policies {\n\t\tif p.Label == \"\" {\n\t\t\tpanic(\"router/score: policy has empty label\")\n\t\t}\n\t\tif p.Description == \"\" {\n\t\t\tpanic(fmt.Sprintf(\"router/score: policy %q has no description\", p.Label))\n\t\t}\n\t}\n\tlabels := make([]string, 0, len(policies))\n\tfor _, p := range policies {\n\t\tlabels = append(labels, p.Label)\n\t}\n\tif opts.ActivationThreshold <= 0 {\n\t\topts.ActivationThreshold = defaultActivationThreshold","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/mudler/LocalAI/blob/44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26/core/services/routing/router/score.go#L149-L185","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Add one or more policies (label + description) under the score classifier config","Verify YAML nesting places policies inside the classifier block","In Go callers, skip classifier construction when policies is empty rather than calling the constructor"],"exampleFix":"# before (yaml)\nrouter:\n  classifier:\n    type: score\n    policies: []\n# after\nrouter:\n  classifier:\n    type: score\n    policies:\n      - label: chat\n        description: \"general conversation\"","handlingStrategy":"validation","validationCode":"if len(cfg.Router.Classifier.Policies) == 0 {\n    return fmt.Errorf(\"router: score classifier requires at least one policy\")\n}","typeGuard":"func hasPolicies(p []ScorePolicy) bool { return len(p) > 0 }","tryCatchPattern":"defer func() { if r := recover(); r != nil { log.Printf(\"router init failed: %v\", r) } }()","preventionTips":["Validate the whole classifier config block before construction","Refuse to enable the router when its policies list is empty","Test config parsing for each classifier type"],"tags":["router","config","panic","construction"],"backgroundTag":null,"analyzedSha":"44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26","analyzedAt":"2026-08-15T10:13:50.291Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}