error.rs 3.5 KB

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