{"record":{"id":"25c1474fee1612d5","repo":"aaif-goose/goose","slug":"missing-activities-in-json-response","errorCode":null,"errorMessage":"Missing 'activities' in json response","messagePattern":"Missing 'activities' in json response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose/src/agents/agent.rs","lineNumber":3852,"sourceCode":"        let clean_content = re\n            .captures(&content)\n            .and_then(|caps| caps.get(1).map(|m| m.as_str()))\n            .unwrap_or(&content)\n            .trim()\n            .to_string();\n\n        let (instructions, activities) =\n            if let Ok(json_content) = serde_json::from_str::<Value>(&clean_content) {\n                let instructions = json_content\n                    .get(\"instructions\")\n                    .ok_or_else(|| anyhow!(\"Missing 'instructions' in json response\"))?\n                    .as_str()\n                    .ok_or_else(|| anyhow!(\"instructions' is not a string\"))?\n                    .to_string();\n\n                let activities = json_content\n                    .get(\"activities\")\n                    .ok_or_else(|| anyhow!(\"Missing 'activities' in json response\"))?\n                    .as_array()\n                    .ok_or_else(|| anyhow!(\"'activities' is not an array'\"))?\n                    .iter()\n                    .map(|act| {\n                        act.as_str()\n                            .map(|s| s.to_string())\n                            .ok_or(anyhow!(\"'activities' array element is not a string\"))\n                    })\n                    .collect::<Result<_, _>>()?;\n\n                (instructions, activities)\n            } else {\n                tracing::warn!(\"Failed to parse JSON, falling back to string parsing\");\n                // If we can't get valid JSON, try string parsing\n                // Use split_once to get the content after \"Instructions:\".\n                let after_instructions = content\n                    .split_once(\"instructions:\")\n                    .map(|(_, rest)| rest)","sourceCodeStart":3834,"sourceCodeEnd":3870,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose/src/agents/agent.rs#L3834-L3870","documentation":"Thrown during recipe response parsing when the JSON object is valid but has no \"activities\" key. Goose's recipe schema requires both \"instructions\" and \"activities\"; a model that omits the activities list produces this error.","triggerScenarios":"Recipe generation where the model returns only {\"instructions\": \"...\"} or renames the field (\"tasks\", \"steps\") instead of \"activities\".","commonSituations":"Models condensing output and dropping fields they deem optional; different model versions interpreting the recipe prompt differently; truncated JSON from long outputs.","solutions":["Retry the recipe generation","Use a stronger model with better instruction following","Reduce input length so the model completes its JSON","Author the recipe manually if generation keeps failing"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Rust — require both keys up front\nlet ok = json.get(\"activities\").is_some() && json.get(\"instructions\").is_some();\nif !ok { /* regenerate */ }","typeGuard":null,"tryCatchPattern":"// Rust — retry on missing-key errors from generation\nmatch generate_recipe(&agent, &conversation).await {\n    Ok(r) => Ok(r),\n    Err(e) if e.to_string().contains(\"Missing 'activities'\") => retry_or_fallback(e),\n    Err(e) => Err(e),\n}","preventionTips":["Give the model a clear example of the expected JSON shape when customizing prompts","Retry generation before assuming a code defect"],"tags":["recipe","json","llm-output","parsing","retryable"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}