alibaba/open-code-review · error

parse grouping JSON: %w

Error message

parse grouping JSON: %w

What it means

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.

Source

Thrown at internal/agent/grouping.go:269

}

func parseGroupingResponse(content string, diffs []model.Diff) ([]FileGroup, error) {
	content = strings.TrimSpace(content)
	// Strip markdown code fences if present
	if strings.HasPrefix(content, "```") {
		lines := strings.Split(content, "\n")
		if len(lines) >= 2 {
			lines = lines[1:]
		}
		if len(lines) > 0 && strings.HasPrefix(strings.TrimSpace(lines[len(lines)-1]), "```") {
			lines = lines[:len(lines)-1]
		}
		content = strings.Join(lines, "\n")
	}

	var resp []groupingResponse
	if err := json.Unmarshal([]byte(content), &resp); err != nil {
		return nil, fmt.Errorf("parse grouping JSON: %w", err)
	}

	diffByPath := make(map[string]model.Diff, len(diffs))
	for _, d := range diffs {
		diffByPath[d.NewPath] = d
	}

	seen := make(map[string]bool, len(diffs))
	var groups []FileGroup

	for _, g := range resp {
		var gDiffs []model.Diff
		for _, f := range g.Files {
			if seen[f] {
				// Skip duplicate — file already assigned to an earlier group
				continue
			}
			d, ok := diffByPath[f]

View on GitHub (pinned to 5cf97d0d15)

Solutions

  1. Read the wrapped %w error to see the exact json.Unmarshal syntax failure and byte offset
  2. Raise maxTokens (default 4096) for large diffs so output is not truncated
  3. Tighten the prompt to forbid any text outside a bare JSON array
  4. Pre-process content to extract the outermost [...] span before unmarshalling
  5. Retry the LLM call — non-deterministic output often parses on a second attempt

Example fix

// before
if err := json.Unmarshal([]byte(content), &resp); err != nil {
    return nil, fmt.Errorf("parse grouping JSON: %w", err)
}
// after: salvage a JSON array embedded in prose
start := strings.Index(content, "["); end := strings.LastIndex(content, "]")
if start >= 0 && end > start { content = content[start : end+1] }
if err := json.Unmarshal([]byte(content), &resp); err != nil {
    return nil, fmt.Errorf("parse grouping JSON: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

func parseableGrouping(content string) error {
    c := strings.TrimSpace(content)
    if i := strings.Index(c, "["); i >= 0 {
        if j := strings.LastIndex(c, "]"); j > i { c = c[i : j+1] }
    }
    var probe []map[string]any
    return json.Unmarshal([]byte(c), &probe)
}

Type guard

func isGroupingShape(v any) bool {
    arr, ok := v.([]any)
    if !ok { return false }
    for _, e := range arr {
        m, ok := e.(map[string]any)
        if !ok { return false }
        if _, ok := m["files"]; !ok { return false }
    }
    return len(arr) > 0
}

Try / catch

groups, err := parseGroupingResponse(content, diffs)
if err != nil {
    var syntaxErr *json.SyntaxError
    if errors.As(err, &syntaxErr) {
        log.Errorf("bad JSON at offset %d: %v", syntaxErr.Offset, syntaxErr)
    }
    return retryOrFallback(diffs)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02). Data as JSON: /api/errors/008df8c932878462. Report an issue: GitHub.