{"record":{"id":"11913f2a80cfb8f5","repo":"nikivdev/code","slug":"could-not-parse-ai-response","errorCode":null,"errorMessage":"Could not parse AI response: '{}'","messagePattern":"Could not parse AI response: '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/ask.rs","lineNumber":461,"sourceCode":"            return Ok(selection);\n        }\n    }\n\n    if cleaned.starts_with(\"f \") || cleaned.starts_with(\"flow \") {\n        let command = normalize_command(cleaned, valid_subcommands)?;\n        return Ok(AskSelection::Command { command });\n    }\n\n    if let Ok(task_name) = extract_task_name(cleaned, tasks) {\n        return Ok(AskSelection::Task { name: task_name });\n    }\n\n    if is_command_like(cleaned, valid_subcommands) {\n        let command = normalize_command(cleaned, valid_subcommands)?;\n        return Ok(AskSelection::Command { command });\n    }\n\n    bail!(\"Could not parse AI response: '{}'\", cleaned);\n}\n\nfn parse_structured_line(\n    raw: &str,\n    tasks: &[DiscoveredTask],\n    valid_subcommands: &HashSet<String>,\n) -> Result<Option<AskSelection>> {\n    if let Some(rest) = raw.strip_prefix(\"task:\") {\n        let task_name = extract_task_name(rest.trim(), tasks)?;\n        return Ok(Some(AskSelection::Task { name: task_name }));\n    }\n    if let Some(rest) = raw.strip_prefix(\"cmd:\") {\n        let command = normalize_command(rest, valid_subcommands)?;\n        return Ok(Some(AskSelection::Command { command }));\n    }\n    if let Some(rest) = raw.strip_prefix(\"command:\") {\n        let command = normalize_command(rest, valid_subcommands)?;\n        return Ok(Some(AskSelection::Command { command }));","sourceCodeStart":443,"sourceCodeEnd":479,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/ask.rs#L443-L479","documentation":"When the cleaned AI response is neither a structured 'task:'/'cmd:' line, an 'f ...' command, a known task name, nor a command-like token whose subcommand is valid, parse_ask_response gives up and includes the raw text in the error so the developer can see what the model said.","triggerScenarios":"The model returns prose ('You could run the build task...'), an unknown/misnamed command ('f buil'), or a task name that doesn't match any discovered task case-insensitively.","commonSituations":"Weak model ignoring the response-format instructions; task list drifted (task renamed/removed since discovery); hallucinated subcommand not in cli_subcommands(); markdown formatting around the answer that wasn't fully stripped.","solutions":["Log the full offending response (it is embedded in the error) and tighten the prompt to demand exact formats","Extend normalize_name-style fuzzy matching so near-miss task names still resolve","Verify the task list sent to the model matches the discovered tasks (cache invalidation)","Fallback to asking the user to pick manually instead of bailing"],"exampleFix":"// before\nbail!(\"Could not parse AI response: '{}'\", cleaned);\n// after\nbail!(\"Could not parse AI response: '{}'. Valid tasks: {:?}; subcommands: {:?}\", cleaned, task_names, valid_subcommands);","handlingStrategy":"fallback","validationCode":"fn response_matches_known_shapes(response: &str, tasks: &[DiscoveredTask], subcommands: &HashSet<String>) -> bool {\n    let cleaned = response.trim().trim_matches('`').trim();\n    cleaned.starts_with(\"task:\") || cleaned.starts_with(\"cmd:\") || cleaned.starts_with(\"command:\")\n        || cleaned.starts_with(\"f \") || cleaned.starts_with(\"flow \")\n        || tasks.iter().any(|t| t.task.name.eq_ignore_ascii_case(cleaned))\n        || subcommands.contains(&cleaned.split_whitespace().next().unwrap_or(\"\").to_ascii_lowercase())\n}","typeGuard":null,"tryCatchPattern":"match parse_ask_response(&response, &tasks, &subcommands) {\n    Err(e) if e.to_string().starts_with(\"Could not parse AI response\") => {\n        eprintln!(\"{}\\nFalling back to manual selection.\", e);\n        prompt_manual_selection(&tasks)\n    }\n    other => other,\n}","preventionTips":["Include exact format instructions and the current task/subcommand list in the prompt","Log the full AI reply when parsing fails (it's embedded in the error)","Fuzzily match task names instead of exact-case matching","Offer an interactive fallback instead of hard-failing"],"tags":["ai","parsing","llm-output"],"backgroundTag":"unparseable-llm-response","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}