{"record":{"id":"063696dea30c0267","repo":"alibaba/open-code-review","slug":"grouping-response-parse-failed-w","errorCode":null,"errorMessage":"grouping response parse failed: %w","messagePattern":"grouping response parse failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/grouping.go","lineNumber":236,"sourceCode":"\t\t\trec.SetError(err, duration)\n\t\t}\n\t\treturn nil, nil, fmt.Errorf(\"grouping LLM call: %w\", err)\n\t}\n\n\tusage = resp.Usage\n\n\tcontent := resp.Content()\n\tif content == \"\" {\n\t\tif rec != nil {\n\t\t\trec.SetError(fmt.Errorf(\"grouping LLM returned empty response\"), duration)\n\t\t}\n\t\treturn nil, usage, fmt.Errorf(\"grouping LLM returned empty response\")\n\t}\n\n\tgroups, err = parseGroupingResponse(content, diffs)\n\tif rec != nil {\n\t\tif err != nil {\n\t\t\trec.SetError(fmt.Errorf(\"grouping response parse failed: %w\", err), duration)\n\t\t} else {\n\t\t\trec.SetResponse(resp, duration)\n\t\t}\n\t}\n\treturn groups, usage, err\n}\n\nfunc buildFileList(diffs []model.Diff) string {\n\tvar sb strings.Builder\n\tfor _, d := range diffs {\n\t\tsb.WriteString(formatDiffEntry(d))\n\t\tsb.WriteString(\"\\n\")\n\t}\n\treturn sb.String()\n}\n\nfunc parseGroupingResponse(content string, diffs []model.Diff) ([]FileGroup, error) {\n\tcontent = strings.TrimSpace(content)","sourceCodeStart":218,"sourceCodeEnd":254,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/agent/grouping.go#L218-L254","documentation":"This error is recorded on the session recorder (not returned to the caller) when the grouping LLM produced non-empty content, but parseGroupingResponse failed to turn that content into valid grouping JSON. The underlying parse error (unknown JSON syntax, wrong shape) is wrapped with %w, so the real cause is chained. The call still returns the raw parse error to the caller; this message is the recorded audit-trail entry.","triggerScenarios":"callGroupingLLM receives a non-empty LLM response whose content, after markdown fence stripping, is not a JSON array of {label, files} objects — e.g. the model answered in prose, returned truncated JSON (max_tokens cut mid-array), or wrapped the JSON in explanatory text beyond the leading/trailing fence.","commonSituations":"Small max_tokens budgets truncating long file lists; weaker models that ignore the 'respond only with JSON' instruction; providers that emit reasoning text before the JSON; responses with trailing commas or comments.","solutions":["Check the recorded session error for the wrapped underlying parse error to see exactly what json.Unmarshal rejected","Increase maxTokens so the grouping JSON is not truncated","Strengthen the grouping prompt to demand only a bare JSON array, or retry the LLM call","Validate the response shape in parseGroupingResponse and surface the offending content snippet in logs"],"exampleFix":"// before (model returned prose + JSON)\ngroups, err = parseGroupingResponse(content, diffs)\n// after (extract first [...] block before parsing)\ncontent = extractJSON(content)\ngroups, err = parseGroupingResponse(content, diffs)","handlingStrategy":"validation","validationCode":"func validGrouping(content string) bool {\n    c := strings.TrimSpace(content)\n    if strings.HasPrefix(c, \"```\") { c = stripFences(c) }\n    var probe []map[string]any\n    return json.Unmarshal([]byte(c), &probe) == nil && len(probe) > 0\n}","typeGuard":"func isJSONArray(raw string) bool {\n    var v []any\n    return json.Unmarshal([]byte(raw), &v) == nil\n}","tryCatchPattern":"groups, _, err := callGroupingLLM(ctx, client, ...)\nif err != nil {\n    log.Warnf(\"grouping parse failed, falling back to single group: %v\", err)\n    groups = fallbackSingleGroup(diffs)\n}","preventionTips":["Set maxTokens generously (files count × ~30 tokens) to avoid truncated JSON","Prompt for 'output ONLY a JSON array, no prose'","Parse leniently: extract the outermost [...] span before json.Unmarshal","Retry the LLM call once on parse failure before failing the run"],"tags":["llm","json-parsing","grouping"],"backgroundTag":"llm-output-not-valid-json","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}