{"record":{"id":"aee4326a6a60dab6","repo":"Hmbown/CodeWhale","slug":"compaction-summary-response-was-unusable-a-placeh","errorCode":null,"errorMessage":"Compaction summary response was unusable: a placeholder was returned.","messagePattern":"Compaction summary response was unusable: a placeholder was returned\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/compaction.rs","lineNumber":1153,"sourceCode":"        .to_ascii_lowercase();\n    if normalized.is_empty() {\n        anyhow::bail!(\n            \"Compaction summary response was unusable: only whitespace or punctuation was returned.\"\n        );\n    }\n    if matches!(\n        normalized.as_str(),\n        \"no summary available\"\n            | \"summary unavailable\"\n            | \"no summary\"\n            | \"n/a\"\n            | \"na\"\n            | \"not available\"\n            | \"i cannot provide a summary\"\n            | \"i can't provide a summary\"\n            | \"unable to provide a summary\"\n    ) {\n        anyhow::bail!(\"Compaction summary response was unusable: a placeholder was returned.\");\n    }\n    Ok(())\n}\n\n/// Drop the oldest history message before retrying an over-window summary\n/// request (Codex parity: `history.remove_first_item()`), plus any tool\n/// results the removal orphans — strict providers reject unpaired results.\nfn drop_oldest_history_messages(messages: &mut Vec<Message>) {\n    if messages.len() <= 1 {\n        return;\n    }\n    messages.remove(0);\n    while messages.len() > 1\n        && messages[0]\n            .content\n            .iter()\n            .any(|block| matches!(block, ContentBlock::ToolResult { .. }))\n    {","sourceCodeStart":1135,"sourceCodeEnd":1171,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/compaction.rs#L1135-L1171","documentation":"The normalized summary matched a known placeholder or refusal string ('no summary available', 'n/a', 'i cannot provide a summary', ...) at crates/tui/src/compaction.rs:1153, so it is treated as unusable rather than persisted as the session checkpoint.","triggerScenarios":"The summary model refuses the request, a safety filter deflects it, or a gateway returns templated fallback text that happens to match the placeholder list.","commonSituations":"Refusals triggered by sensitive session content; models that answer literally when they cannot comply; identical canned responses from caching layers.","solutions":["Retry - refusals are often stochastic","Switch or adjust the compaction model (one less prone to refusals)","If content in the session triggers refusals, compact earlier so the offending context is smaller","Check whether an intermediary cache is returning a canned response"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Reject known placeholder texts before accepting a summary\nconst PLACEHOLDERS: [&str; 8] = [\"no summary available\",\"summary unavailable\",\"no summary\",\"n/a\",\"na\",\"not available\",\"i cannot provide a summary\",\"i can't provide a summary\"];\nfn is_placeholder(s: &str) -> bool { PLACEHOLDERS.contains(&s.trim().to_ascii_lowercase().as_str()) }","typeGuard":"fn is_placeholder_summary_error(msg: &str) -> bool {\n    msg.contains(\"a placeholder was returned\")\n}","tryCatchPattern":"// Fall back to a second model; refusals follow the model, not the input\nmatch compact().await {\n    Err(e) if is_placeholder_summary_error(&e.to_string()) => compact_with_model(alt_model).await,\n    r => r,\n}","preventionTips":["Pick compaction models with low refusal rates for code content","Compact before sessions accumulate content that trips filters"],"tags":["compaction","validation","refusal","placeholder"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}