{"record":{"id":"3684e65282e69889","repo":"alibaba/open-code-review","slug":"scan-max-tokens-must-be-positive","errorCode":null,"errorMessage":"scan: max_tokens must be positive","messagePattern":"scan: max_tokens must be positive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/template/template.go","lineNumber":331,"sourceCode":"\tif t.MaxTokens <= 0 {\n\t\treturn fmt.Errorf(\"max_tokens must be positive\")\n\t}\n\tif t.MaxToolRequestTimes <= 0 {\n\t\treturn fmt.Errorf(\"max_tool_request_times must be positive\")\n\t}\n\tif t.MaxReviewRounds < 0 {\n\t\treturn fmt.Errorf(\"max_review_rounds must not be negative\")\n\t}\n\tif len(t.MainTask.Messages) == 0 {\n\t\treturn fmt.Errorf(\"main_task.messages must not be empty\")\n\t}\n\treturn nil\n}\n\n// Validate checks that a ScanTemplate has the minimum fields populated.\nfunc (t *ScanTemplate) Validate() error {\n\tif t.MaxTokens <= 0 {\n\t\treturn fmt.Errorf(\"scan: max_tokens must be positive\")\n\t}\n\tif t.MaxToolRequestTimes <= 0 {\n\t\treturn fmt.Errorf(\"scan: max_tool_request_times must be positive\")\n\t}\n\tif len(t.MainTask.Messages) == 0 {\n\t\treturn fmt.Errorf(\"scan: main_task.messages must not be empty\")\n\t}\n\treturn nil\n}\n\n// LlmConversation is a preset prompt with settings.\ntype LlmConversation struct {\n\tMessages []ChatMessage `json:\"messages\"`\n}\n\n// ChatMessage represents a single message in a conversation.\ntype ChatMessage struct {\n\tRole    string `json:\"role\"`","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/config/template/template.go#L313-L349","documentation":"ScanTemplate.Validate performs the same minimum checks as Template.Validate but for the scan template, prefixing messages with 'scan: ' so the failing template kind is identifiable. 'scan: max_tokens must be positive' means the scan template's MaxTokens is zero or negative, so the scan review request cannot be budgeted.","triggerScenarios":"Calling (*ScanTemplate).Validate() with t.MaxTokens <= 0 — typically a scan_template.json embedded manifest missing MAX_TOKENS or a hand-built ScanTemplate left at its zero value, e.g. before executeScan runs.","commonSituations":"Customizing scan_template.json and dropping MAX_TOKENS; constructing a ScanTemplate in tests (TestLoadScanDefault_*/TestRunScanPreview paths) without setting the token budget; copying fields from task_template.json to a ScanTemplate while missing the scan-specific keys.","solutions":["Set MaxTokens to a positive value on the ScanTemplate before Validate","Add \"MAX_TOKENS\": <n> (n > 0) to scan_template.json and rebuild if the value comes from the embedded manifest","Compare against the shipped default scan_template.json to ensure no required numeric fields were dropped during customization","Call Validate immediately after LoadScanDefault/manifest copy so errors surface before executeScan issues any request"],"exampleFix":"// before (scan_template.json)\n{\"MAX_TOOL_REQUEST_TIMES\": 5, \"MAIN_TASK\": {...}}\n// after\n{\"MAX_TOKENS\": 8192, \"MAX_TOOL_REQUEST_TIMES\": 5, \"MAIN_TASK\": {...}}","handlingStrategy":"validation","validationCode":"if stpl.MaxTokens <= 0 {\n\treturn fmt.Errorf(\"scan MaxTokens must be > 0 before Validate, got %d\", stpl.MaxTokens)\n}\nif err := stpl.Validate(); err != nil { return err }","typeGuard":null,"tryCatchPattern":"if err := stpl.Validate(); err != nil {\n\tif strings.Contains(err.Error(), \"scan: max_tokens must be positive\") {\n\t\tstpl.MaxTokens = 8192\n\t\terr = stpl.Validate()\n\t}\n\tif err != nil { return err }\n}","preventionTips":["After LoadScanDefault, always call Validate before executeScan","When customizing scan_template.json, diff against the shipped default to avoid dropping numeric budgets","Treat the 'scan: ' error prefix as identifying ScanTemplate checks in log triage"],"tags":["go","validation","scan","config"],"backgroundTag":"invalid-config-value","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}