Skip to main content

synapse/
error.rs

1//! Gateway error type → OpenAI-shaped JSON + HTTP status.
2
3#[cfg(feature = "server")]
4use axum::http::StatusCode;
5#[cfg(feature = "server")]
6use axum::response::{IntoResponse, Response};
7#[cfg(feature = "server")]
8use axum::Json;
9#[cfg(feature = "server")]
10use serde_json::json;
11use thiserror::Error;
12
13#[derive(Debug, Error)]
14pub enum GatewayError {
15    #[error("unknown model alias '{0}'")]
16    UnknownModel(String),
17    #[error("native feature '{feature}' is not available on route '{route}'")]
18    NativeFeatureUnsupported { feature: String, route: String },
19    #[error("invalid request: {0}")]
20    BadRequest(String),
21    #[error("all legs of route '{route}' failed")]
22    AllLegsFailed {
23        route: String,
24        failures: Vec<LegFailure>,
25    },
26    #[error("all providers for route '{0}' are unavailable")]
27    AllCircuitsOpen(String),
28    #[error("upstream timed out")]
29    UpstreamTimeout,
30    #[error("upstream error {status}: {body}")]
31    Upstream { status: u16, body: String },
32}
33
34#[derive(Debug, Clone, serde::Serialize)]
35pub struct LegFailure {
36    pub provider: String,
37    pub model: String,
38    pub message: String,
39}
40
41impl GatewayError {
42    #[cfg(feature = "server")]
43    pub fn status(&self) -> StatusCode {
44        match self {
45            GatewayError::UnknownModel(_) => StatusCode::NOT_FOUND,
46            GatewayError::NativeFeatureUnsupported { .. } | GatewayError::BadRequest(_) => {
47                StatusCode::BAD_REQUEST
48            }
49            GatewayError::AllLegsFailed { .. } | GatewayError::Upstream { .. } => {
50                StatusCode::BAD_GATEWAY
51            }
52            GatewayError::AllCircuitsOpen(_) => StatusCode::SERVICE_UNAVAILABLE,
53            GatewayError::UpstreamTimeout => StatusCode::GATEWAY_TIMEOUT,
54        }
55    }
56
57    #[cfg(feature = "server")]
58    fn code(&self) -> &'static str {
59        match self {
60            GatewayError::UnknownModel(_) => "model_not_found",
61            GatewayError::NativeFeatureUnsupported { .. } => "native_feature_unsupported",
62            GatewayError::BadRequest(_) => "invalid_request_error",
63            GatewayError::AllLegsFailed { .. } => "all_legs_failed",
64            GatewayError::AllCircuitsOpen(_) => "circuit_open",
65            GatewayError::UpstreamTimeout => "upstream_timeout",
66            GatewayError::Upstream { .. } => "upstream_error",
67        }
68    }
69}
70
71#[cfg(feature = "server")]
72impl IntoResponse for GatewayError {
73    fn into_response(self) -> Response {
74        let mut error = json!({
75            "type": self.code(),
76            "message": self.to_string(),
77            "code": self.code(),
78        });
79        if let GatewayError::AllLegsFailed { failures, .. } = &self {
80            error["failures"] = json!(failures);
81        }
82        (self.status(), Json(json!({ "error": error }))).into_response()
83    }
84}