Skip to content

enhancement: repair truncated JSON from LLM responses #186

Description

@ajianaz

Description

When reviewing large diffs, the LLM response can be truncated due to max_tokens limit. The current repair_json_string() function only fixes invalid escape sequences but cannot handle truncated JSON (EOF while parsing a string).

Code References

File Line Current State
src/engine/llm.rs 723-733 repair_json_string() — only calls repair_invalid_escapes()
src/engine/llm.rs 738-810 repair_invalid_escapes() — state machine for \s\\s
src/engine/llm.rs 650-654 Parse path: repair_json_string()serde_json::from_str()CoraError::LlmParse
src/engine/llm.rs 668-671 Same path for scan responses

No truncated JSON handler exists. If truncated → parse fails completely → all findings lost.

Error

LLM review failed: failed to parse LLM JSON response: EOF while parsing a string at line 18 column 25

Fix Plan

  1. Add repair_truncated_json() function before serde_json::from_str():
    • Count unclosed {, [, " brackets
    • Close them in reverse order
    • Truncate last partial string value
    • Keep already-parsed findings intact
  2. Fallback: partial results — if repair fails, attempt to parse everything before truncation point + return partial results with warning
  3. Add integration test — truncated JSON samples that should repair correctly

Implementation

fn repair_truncated_json(json: &str) -> String {
    let mut stack: Vec<char> = Vec::new();
    let mut in_string = false;
    let mut escape_next = false;
    
    for ch in json.chars() {
        if escape_next { escape_next = false; continue; }
        match ch {
            ' if !escape_next => in_string = !in_string,
            '{' | '[' if !in_string => stack.push(ch),
            '}' if !in_string => { stack.pop(); }
            ']' if !in_string => { stack.pop(); }
            '\' if in_string => escape_next = true,
            _ => {}
        }
    }
    
    let mut repaired = json.to_string();
    // Close unclosed string
    if in_string { repaired.push('); }
    // Close brackets in reverse order
    for ch in stack.iter().rev() {
        match ch {
            { => repaired.push(}),
            [ => repaired.push(]),
            _ => {}
        }
    }
    repaired
}

Target

  • Patch release: v0.4.4
  • Files: src/engine/llm.rs (~80 lines added)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions