{"record":{"id":"95fa09922a2b382e","repo":"nikivdev/code","slug":"kimi-returned-empty-review-output-no-text-content","errorCode":null,"errorMessage":"kimi returned empty review output (no text content in response)","messagePattern":"kimi returned empty review output \\(no text content in response\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/commit.rs","lineNumber":6071,"sourceCode":"        let stdout_text = String::from_utf8_lossy(&stdout_bytes);\n        let error_msg = if stderr_text.trim().is_empty() {\n            stdout_text.trim()\n        } else {\n            stderr_text.trim()\n        };\n        bail!(\"kimi review failed: {}\", error_msg);\n    }\n\n    let stdout_text = String::from_utf8_lossy(&stdout_bytes).trim().to_string();\n    if stdout_text.is_empty() {\n        bail!(\"kimi returned empty output\");\n    }\n\n    // Parse the stream-json output from kimi\n    // Format: {\"role\":\"assistant\",\"content\":[{\"type\":\"think\",\"think\":\"...\"},{\"type\":\"text\",\"text\":\"...\"}]}\n    let result = extract_kimi_text_content(&stdout_text).unwrap_or_else(|| stdout_text.clone());\n    if result.is_empty() {\n        bail!(\"kimi returned empty review output (no text content in response)\");\n    }\n\n    // Try to parse JSON from output\n    let mut review_json = parse_review_json(&result);\n    let future_tasks = review_json\n        .as_ref()\n        .map(|json| normalize_future_tasks(&json.future_tasks))\n        .unwrap_or_default();\n    let mut summary = review_json.as_ref().and_then(|r| r.summary.clone());\n    let quality = review_json.as_mut().and_then(|r| r.quality.take());\n    let (mut issues_found, mut issues) = if let Some(ref json) = review_json {\n        (json.issues_found, json.issues.clone())\n    } else {\n        let lowered = result.to_lowercase();\n        let has_issues = lowered.contains(\"bug\")\n            || lowered.contains(\"issue\")\n            || lowered.contains(\"error\")\n            || lowered.contains(\"problem\")","sourceCodeStart":6053,"sourceCodeEnd":6089,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/commit.rs#L6053-L6089","documentation":"Thrown when kimi exits successfully with non-empty stdout, but after extracting the assistant text content from the stream-JSON response the resulting review text is empty — i.e. the response contained no usable 'text' content blocks.","triggerScenarios":"extract_kimi_text_content() on the stream-json stdout yields nothing (only 'think' blocks, no 'text' block) and the raw fallback is also empty after parsing.","commonSituations":"kimi model returned only reasoning/think content with no final answer; kimi response format changed in a newer CLI version, breaking the parser; truncated response due to token limits; unusual prompt producing no text output.","solutions":["Update the kimi CLI and retry — the stream-JSON schema may have changed relative to extract_kimi_text_content().","Lower review scope/diff size to avoid truncation and encourage a text answer.","Adjust review instructions/prompt to require a textual verdict.","Check kimi raw output manually (run the command with the same flags) and report a parser bug if 'text' content exists but isn't extracted."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"fn has_text_content(stream_json: &str) -> bool {\n    serde_json::from_str::<serde_json::Value>(stream_json).ok()\n        .and_then(|v| v.get(\"content\").cloned())\n        .and_then(|c| c.as_array().cloned())\n        .map(|blocks| blocks.iter().any(|b| b.get(\"type\").and_then(|t| t.as_str()) == Some(\"text\")\n            && b.get(\"text\").and_then(|t| t.as_str()).map(|s| !s.trim().is_empty()).unwrap_or(false)))\n        .unwrap_or(false)\n}","typeGuard":"fn extract_nonempty_text(v: &serde_json::Value) -> Option<&str> {\n    v.get(\"content\")?.as_array()?\n        .iter()\n        .find_map(|b| {\n            let t = b.get(\"text\")?.as_str()?;\n            (!t.trim().is_empty()).then_some(t)\n        })\n}","tryCatchPattern":"match run_kimi_review(&diff).await {\n    Err(e) if e.to_string().contains(\"empty review output\") => {\n        eprintln!(\"kimi gave no text content; retrying with explicit instruction\");\n        run_kimi_review(&format!(\"{diff}\\n\\nRespond with a textual review.\")).await\n    }\n    other => other,\n}","preventionTips":["Require a textual answer in review instructions/prompts","Update kimi CLI in lockstep with parser expectations (extract_kimi_text_content)","Add a parser regression test against real kimi stream-JSON samples","Detect think-only responses early and re-prompt instead of failing at parse time"],"tags":["kimi","parsing","stream-json","ai-review"],"backgroundTag":"ai-cli-empty-output","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}