error.rs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. use std::env::VarError;
  2. use std::fmt::{Display, Formatter};
  3. use std::time::Duration;
  4. #[derive(Debug)]
  5. pub enum ApiError {
  6. MissingApiKey,
  7. ExpiredOAuthToken,
  8. Auth(String),
  9. InvalidApiKeyEnv(VarError),
  10. Http(reqwest::Error),
  11. Io(std::io::Error),
  12. Json(serde_json::Error),
  13. Api {
  14. status: reqwest::StatusCode,
  15. error_type: Option<String>,
  16. message: Option<String>,
  17. body: String,
  18. retryable: bool,
  19. },
  20. RetriesExhausted {
  21. attempts: u32,
  22. last_error: Box<ApiError>,
  23. },
  24. InvalidSseFrame(&'static str),
  25. BackoffOverflow {
  26. attempt: u32,
  27. base_delay: Duration,
  28. },
  29. }
  30. impl ApiError {
  31. #[must_use]
  32. pub fn is_retryable(&self) -> bool {
  33. match self {
  34. Self::Http(error) => error.is_connect() || error.is_timeout() || error.is_request(),
  35. Self::Api { retryable, .. } => *retryable,
  36. Self::RetriesExhausted { last_error, .. } => last_error.is_retryable(),
  37. Self::MissingApiKey
  38. | Self::ExpiredOAuthToken
  39. | Self::Auth(_)
  40. | Self::InvalidApiKeyEnv(_)
  41. | Self::Io(_)
  42. | Self::Json(_)
  43. | Self::InvalidSseFrame(_)
  44. | Self::BackoffOverflow { .. } => false,
  45. }
  46. }
  47. }
  48. impl Display for ApiError {
  49. fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
  50. match self {
  51. Self::MissingApiKey => {
  52. write!(
  53. f,
  54. "ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY is not set; export one before calling the Anthropic API"
  55. )
  56. }
  57. Self::ExpiredOAuthToken => {
  58. write!(
  59. f,
  60. "saved OAuth token is expired and no refresh token is available"
  61. )
  62. }
  63. Self::Auth(message) => write!(f, "auth error: {message}"),
  64. Self::InvalidApiKeyEnv(error) => {
  65. write!(
  66. f,
  67. "failed to read ANTHROPIC_AUTH_TOKEN / ANTHROPIC_API_KEY: {error}"
  68. )
  69. }
  70. Self::Http(error) => write!(f, "http error: {error}"),
  71. Self::Io(error) => write!(f, "io error: {error}"),
  72. Self::Json(error) => write!(f, "json error: {error}"),
  73. Self::Api {
  74. status,
  75. error_type,
  76. message,
  77. body,
  78. ..
  79. } => match (error_type, message) {
  80. (Some(error_type), Some(message)) => {
  81. write!(
  82. f,
  83. "anthropic api returned {status} ({error_type}): {message}"
  84. )
  85. }
  86. _ => write!(f, "anthropic api returned {status}: {body}"),
  87. },
  88. Self::RetriesExhausted {
  89. attempts,
  90. last_error,
  91. } => write!(
  92. f,
  93. "anthropic api failed after {attempts} attempts: {last_error}"
  94. ),
  95. Self::InvalidSseFrame(message) => write!(f, "invalid sse frame: {message}"),
  96. Self::BackoffOverflow {
  97. attempt,
  98. base_delay,
  99. } => write!(
  100. f,
  101. "retry backoff overflowed on attempt {attempt} with base delay {base_delay:?}"
  102. ),
  103. }
  104. }
  105. }
  106. impl std::error::Error for ApiError {}
  107. impl From<reqwest::Error> for ApiError {
  108. fn from(value: reqwest::Error) -> Self {
  109. Self::Http(value)
  110. }
  111. }
  112. impl From<std::io::Error> for ApiError {
  113. fn from(value: std::io::Error) -> Self {
  114. Self::Io(value)
  115. }
  116. }
  117. impl From<serde_json::Error> for ApiError {
  118. fn from(value: serde_json::Error) -> Self {
  119. Self::Json(value)
  120. }
  121. }
  122. impl From<VarError> for ApiError {
  123. fn from(value: VarError) -> Self {
  124. Self::InvalidApiKeyEnv(value)
  125. }
  126. }