Preserve recovery guidance for retried context-window failures

The CLI already reframes direct preflight and provider oversized-request
errors, but retry-wrapped provider failures still fell back to the generic
retry-exhausted surface because the user-visible formatter keyed off the
safe failure class. Route formatting through nested context-window
detection so wrapped provider failures keep the same compact/reduce-scope
guidance.

Constraint: Keep the fix UX-scoped without widening broader failure classification behavior
Rejected: Reorder safe_failure_class for all RetriesExhausted errors | broader semantic change than needed for this issue
Confidence: high
Scope-risk: narrow
Directive: Keep context-window rendering keyed to nested error inspection so provider wrappers do not lose recovery guidance
Tested: cargo fmt --check; cargo test -p rusty-claude-cli context_window; cargo test -p api oversized
Not-tested: Full workspace test suite
This commit is contained in:
Yeachan-Heo
2026-04-06 06:15:50 +00:00
parent 6bd464bbe7
commit 8ff9c1b15a

View File

@@ -5742,7 +5742,7 @@ impl ApiClient for AnthropicRuntimeClient {
}
fn format_user_visible_api_error(session_id: &str, error: &api::ApiError) -> String {
if error.safe_failure_class() == "context_window" {
if error.is_context_window_failure() {
format_context_window_blocked_error(session_id, error)
} else if error.is_generic_fatal_wrapper() {
let mut qualifiers = vec![format!("session {session_id}")];
@@ -6986,6 +6986,39 @@ mod tests {
);
}
#[test]
fn retry_wrapped_context_window_errors_keep_recovery_guidance() {
let error = ApiError::RetriesExhausted {
attempts: 2,
last_error: Box::new(ApiError::Api {
status: "413".parse().expect("status"),
error_type: Some("invalid_request_error".to_string()),
message: Some("Request is too large for this model's context window.".to_string()),
request_id: Some("req_ctx_retry_789".to_string()),
body: String::new(),
retryable: false,
}),
};
let rendered = format_user_visible_api_error("session-issue-32", &error);
assert!(rendered.contains("Context window blocked"), "{rendered}");
assert!(rendered.contains("context_window_blocked"), "{rendered}");
assert!(
rendered.contains("Trace req_ctx_retry_789"),
"{rendered}"
);
assert!(
rendered
.contains("Detail Request is too large for this model's context window."),
"{rendered}"
);
assert!(rendered.contains("Compact /compact"), "{rendered}");
assert!(
rendered.contains("Resume compact claw --resume session-issue-32 /compact"),
"{rendered}"
);
}
fn temp_dir() -> PathBuf {
use std::sync::atomic::{AtomicU64, Ordering};