Expose anthropic API errors to the client (#16129)
Max Brunsfeld
and
Marshall
created 1 year ago
Now, when an anthropic request is invalid or anthropic's API is down,
we'll expose that to the user instead of just returning a generic 500.
Release Notes:
- N/A
Co-authored-by: Marshall <marshall@zed.dev>
Change summary
crates/collab/src/llm.rs | 24 ++++++++++++++--------
crates/language_model/src/provider/cloud.rs | 8 ++++--
2 files changed, 20 insertions(+), 12 deletions(-)
Detailed changes
@@ -207,16 +207,22 @@ async fn perform_completion(
)
.await
.map_err(|err| match err {
- anthropic::AnthropicError::ApiError(ref api_error) => {
- if api_error.code() == Some(anthropic::ApiErrorCode::RateLimitError) {
- return Error::http(
- StatusCode::TOO_MANY_REQUESTS,
- "Upstream Anthropic rate limit exceeded.".to_string(),
- );
+ anthropic::AnthropicError::ApiError(ref api_error) => match api_error.code() {
+ Some(anthropic::ApiErrorCode::RateLimitError) => Error::http(
+ StatusCode::TOO_MANY_REQUESTS,
+ "Upstream Anthropic rate limit exceeded.".to_string(),
+ ),
+ Some(anthropic::ApiErrorCode::InvalidRequestError) => {
+ Error::http(StatusCode::BAD_REQUEST, api_error.message.clone())
}
-
- Error::Internal(anyhow!(err))
- }
+ Some(anthropic::ApiErrorCode::OverloadedError) => {
+ Error::http(StatusCode::SERVICE_UNAVAILABLE, api_error.message.clone())
+ }
+ Some(_) => {
+ Error::http(StatusCode::INTERNAL_SERVER_ERROR, api_error.message.clone())
+ }
+ None => Error::Internal(anyhow!(err)),
+ },
anthropic::AnthropicError::Other(err) => Error::Internal(err),
})?;
@@ -20,7 +20,7 @@ use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use settings::{Settings, SettingsStore};
use smol::{
- io::BufReader,
+ io::{AsyncReadExt, BufReader},
lock::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard},
};
use std::{future, sync::Arc};
@@ -334,7 +334,7 @@ impl CloudLanguageModel {
.header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {token}"))
.body(serde_json::to_string(&body)?.into())?;
- let response = http_client.send(request).await?;
+ let mut response = http_client.send(request).await?;
if response.status().is_success() {
break response;
} else if !did_retry
@@ -346,8 +346,10 @@ impl CloudLanguageModel {
did_retry = true;
token = llm_api_token.refresh(&client).await?;
} else {
+ let mut body = String::new();
+ response.body_mut().read_to_string(&mut body).await?;
break Err(anyhow!(
- "cloud language model completion failed with status {}",
+ "cloud language model completion failed with status {}: {body}",
response.status()
))?;
}