{"record":{"id":"008df8c932878462","repo":"alibaba/open-code-review","slug":"parse-grouping-json-w","errorCode":null,"errorMessage":"parse grouping JSON: %w","messagePattern":"parse grouping JSON: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/grouping.go","lineNumber":269,"sourceCode":"}\n\nfunc parseGroupingResponse(content string, diffs []model.Diff) ([]FileGroup, error) {\n\tcontent = strings.TrimSpace(content)\n\t// Strip markdown code fences if present\n\tif strings.HasPrefix(content, \"```\") {\n\t\tlines := strings.Split(content, \"\\n\")\n\t\tif len(lines) >= 2 {\n\t\t\tlines = lines[1:]\n\t\t}\n\t\tif len(lines) > 0 && strings.HasPrefix(strings.TrimSpace(lines[len(lines)-1]), \"```\") {\n\t\t\tlines = lines[:len(lines)-1]\n\t\t}\n\t\tcontent = strings.Join(lines, \"\\n\")\n\t}\n\n\tvar resp []groupingResponse\n\tif err := json.Unmarshal([]byte(content), &resp); err != nil {\n\t\treturn nil, fmt.Errorf(\"parse grouping JSON: %w\", err)\n\t}\n\n\tdiffByPath := make(map[string]model.Diff, len(diffs))\n\tfor _, d := range diffs {\n\t\tdiffByPath[d.NewPath] = d\n\t}\n\n\tseen := make(map[string]bool, len(diffs))\n\tvar groups []FileGroup\n\n\tfor _, g := range resp {\n\t\tvar gDiffs []model.Diff\n\t\tfor _, f := range g.Files {\n\t\t\tif seen[f] {\n\t\t\t\t// Skip duplicate — file already assigned to an earlier group\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\td, ok := diffByPath[f]","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/agent/grouping.go#L251-L287","documentation":"parseGroupingResponse wraps json.Unmarshal failures when converting the LLM's response content into []groupingResponse. Before unmarshalling it trims whitespace and strips a single markdown code fence, but any remaining non-JSON or malformed JSON causes this error. It is the underlying cause of the 'grouping response parse failed' recorded message in callGroupingLLM.","triggerScenarios":"The LLM content is not a syntactically valid JSON array — truncated output, prose around the JSON, single quotes instead of double, trailing commas, or a JSON object where an array is expected.","commonSituations":"max_tokens too low so the array is cut off mid-file; model adds commentary like 'Here is the grouping:'; provider returns the JSON inside nested code fences; locale/format drift in a model upgrade.","solutions":["Read the wrapped %w error to see the exact json.Unmarshal syntax failure and byte offset","Raise maxTokens (default 4096) for large diffs so output is not truncated","Tighten the prompt to forbid any text outside a bare JSON array","Pre-process content to extract the outermost [...] span before unmarshalling","Retry the LLM call — non-deterministic output often parses on a second attempt"],"exampleFix":"// before\nif err := json.Unmarshal([]byte(content), &resp); err != nil {\n    return nil, fmt.Errorf(\"parse grouping JSON: %w\", err)\n}\n// after: salvage a JSON array embedded in prose\nstart := strings.Index(content, \"[\"); end := strings.LastIndex(content, \"]\")\nif start >= 0 && end > start { content = content[start : end+1] }\nif err := json.Unmarshal([]byte(content), &resp); err != nil {\n    return nil, fmt.Errorf(\"parse grouping JSON: %w\", err)\n}","handlingStrategy":"validation","validationCode":"func parseableGrouping(content string) error {\n    c := strings.TrimSpace(content)\n    if i := strings.Index(c, \"[\"); i >= 0 {\n        if j := strings.LastIndex(c, \"]\"); j > i { c = c[i : j+1] }\n    }\n    var probe []map[string]any\n    return json.Unmarshal([]byte(c), &probe)\n}","typeGuard":"func isGroupingShape(v any) bool {\n    arr, ok := v.([]any)\n    if !ok { return false }\n    for _, e := range arr {\n        m, ok := e.(map[string]any)\n        if !ok { return false }\n        if _, ok := m[\"files\"]; !ok { return false }\n    }\n    return len(arr) > 0\n}","tryCatchPattern":"groups, err := parseGroupingResponse(content, diffs)\nif err != nil {\n    var syntaxErr *json.SyntaxError\n    if errors.As(err, &syntaxErr) {\n        log.Errorf(\"bad JSON at offset %d: %v\", syntaxErr.Offset, syntaxErr)\n    }\n    return retryOrFallback(diffs)\n}","preventionTips":["Increase maxTokens for large diffs (default 4096 truncates long file lists)","Demand bare JSON in the prompt and strip markdown fences defensively","Salvage the outermost [...] before unmarshalling","Log the raw content on failure so the offending text is visible"],"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"}