diff --git a/crates/agent_servers/src/acp.rs b/crates/agent_servers/src/acp.rs index 07dd4214055180a1f1887294a5ab5c4cdd42a0f5..87bb39956b68f20eac8222398348b82b606d33a6 100644 --- a/crates/agent_servers/src/acp.rs +++ b/crates/agent_servers/src/acp.rs @@ -381,11 +381,34 @@ impl AgentConnection for AcpConnection { Ok(response) => Ok(response), Err(err) => { if err.code != acp::ErrorCode::INTERNAL_ERROR.code { + // Intercept Unauthorized to trigger auth UI instead of retrying + if let Some(data) = &err.data { + #[derive(Deserialize)] + #[serde(deny_unknown_fields)] + struct ErrorDetails { + details: Box, + } + if let Ok(ErrorDetails { details }) = + serde_json::from_value::(data.clone()) + { + if details.contains("401") || details.contains("Unauthorized") { + return Err(anyhow!(acp_thread::AuthRequired::new())); + } + } + } + if err.message.contains("401") || err.message.contains("Unauthorized") { + return Err(anyhow!(acp::Error::auth_required())); + } anyhow::bail!(err) } let Some(data) = &err.data else { - anyhow::bail!(err) + // INTERNAL_ERROR without data but Unauthorized in the message + if err.message.contains("401") || err.message.contains("Unauthorized") { + return Err(anyhow!(acp::Error::auth_required())); + } else { + anyhow::bail!(err) + } }; // Temporary workaround until the following PR is generally available: @@ -407,11 +430,20 @@ impl AgentConnection for AcpConnection { stop_reason: acp::StopReason::Cancelled, meta: None, }) + } else if details.contains("401") || details.contains("Unauthorized") { + Err(anyhow!(acp::Error::auth_required())) } else { Err(anyhow!(details)) } } - Err(_) => Err(anyhow!(err)), + Err(_) => { + let msg = err.message.as_str(); + if msg.contains("401") || msg.contains("Unauthorized") { + Err(anyhow!(acp_thread::AuthRequired::new())) + } else { + Err(anyhow!(err)) + } + } } } }