{"record":{"id":"34493758f2dd7b33","repo":"nikivdev/code","slug":"ai-returned-an-empty-response","errorCode":null,"errorMessage":"AI returned an empty response.","messagePattern":"AI returned an empty response\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/ask.rs","lineNumber":428,"sourceCode":"                agent_id\n            ));\n        }\n    }\n\n    prompt.push_str(&format!(\"\\nUser query: {}\\n\", query));\n    prompt.push_str(\"Answer:\");\n\n    prompt\n}\n\nfn parse_ask_response(\n    response: &str,\n    tasks: &[DiscoveredTask],\n    valid_subcommands: &HashSet<String>,\n) -> Result<AskSelection> {\n    let cleaned = response.trim().trim_matches('`').trim();\n    if cleaned.is_empty() {\n        bail!(\"AI returned an empty response.\");\n    }\n\n    if let Some(selection) = parse_structured_line(cleaned, tasks, valid_subcommands)? {\n        return Ok(selection);\n    }\n\n    // Some models emit reasoning wrappers (e.g. <think>...</think>) before the\n    // final machine-readable answer. Scan lines and parse the first valid one.\n    for line in cleaned.lines() {\n        let candidate = line.trim();\n        if candidate.is_empty() {\n            continue;\n        }\n        if let Some(selection) = parse_structured_line(candidate, tasks, valid_subcommands)? {\n            return Ok(selection);\n        }\n    }\n","sourceCodeStart":410,"sourceCodeEnd":446,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/ask.rs#L410-L446","documentation":"parse_ask_response is the single funnel through which the AI's natural-language answer is interpreted. Before any parsing it trims backticks/whitespace, and if nothing remains it bails — the model produced no usable content, so there is no selection to return.","triggerScenarios":"The AI HTTP call succeeds but returns an empty string or only whitespace/backticks; a response containing just '```' or '``'; a proxy or provider returning an empty body with 200 OK.","commonSituations":"Model overloaded or refusing and yielding an empty completion; prompt too restrictive; misconfigured API gateway stripping the body.","solutions":["Retry the AI request (empty responses are usually transient)","Check the raw response body/logs before parsing to confirm what the provider actually returned","Strengthen the prompt to force a non-empty structured answer ('task: <name>' or 'cmd: <command>')","If retrying fails, surface a 'try again' path to the user rather than crashing"],"exampleFix":"// before\nlet cleaned = response.trim().trim_matches('`').trim();\nif cleaned.is_empty() {\n    bail!(\"AI returned an empty response.\");\n}\n// after\nlet cleaned = response.trim().trim_matches('`').trim();\nif cleaned.is_empty() {\n    eprintln!(\"AI returned an empty response, retrying...\");\n    return retry_ask(...);\n}","handlingStrategy":"retry","validationCode":"fn ai_response_usable(response: &str) -> bool {\n    !response.trim().trim_matches('`').trim().is_empty()\n}","typeGuard":null,"tryCatchPattern":"match parse_ask_response(&response, &tasks, &subcommands) {\n    Err(e) if e.to_string().contains(\"empty response\") => {\n        // retry up to N times with backoff\n    }\n    other => other,\n}","preventionTips":["Retry empty AI responses — they are usually transient","Log the raw HTTP body before parsing","Strengthen the prompt to require a structured non-empty answer","Set a max-temp/deterministic setting to reduce empty completions"],"tags":["ai","empty-response","parsing"],"backgroundTag":"ai-empty-response","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}