servers/
error.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::any::Any;
16use std::net::SocketAddr;
17use std::string::FromUtf8Error;
18
19use axum::http::StatusCode as HttpStatusCode;
20use axum::response::{IntoResponse, Response};
21use axum::{http, Json};
22use base64::DecodeError;
23use common_error::define_into_tonic_status;
24use common_error::ext::{BoxedError, ErrorExt};
25use common_error::status_code::StatusCode;
26use common_macro::stack_trace_debug;
27use common_telemetry::{error, warn};
28use common_time::Duration;
29use datafusion::error::DataFusionError;
30use datatypes::prelude::ConcreteDataType;
31use headers::ContentType;
32use http::header::InvalidHeaderValue;
33use query::parser::PromQuery;
34use serde_json::json;
35use snafu::{Location, Snafu};
36
37#[derive(Snafu)]
38#[snafu(visibility(pub))]
39#[stack_trace_debug]
40pub enum Error {
41    #[snafu(display("Failed to bind address: {}", addr))]
42    AddressBind {
43        addr: SocketAddr,
44        #[snafu(source)]
45        error: std::io::Error,
46        #[snafu(implicit)]
47        location: Location,
48    },
49
50    #[snafu(display("Arrow error"))]
51    Arrow {
52        #[snafu(source)]
53        error: arrow_schema::ArrowError,
54    },
55
56    #[snafu(display("Internal error: {}", err_msg))]
57    Internal { err_msg: String },
58
59    #[snafu(display("Unsupported data type: {}, reason: {}", data_type, reason))]
60    UnsupportedDataType {
61        data_type: ConcreteDataType,
62        reason: String,
63    },
64
65    #[snafu(display("Internal IO error"))]
66    InternalIo {
67        #[snafu(source)]
68        error: std::io::Error,
69    },
70
71    #[snafu(display("Tokio IO error: {}", err_msg))]
72    TokioIo {
73        err_msg: String,
74        #[snafu(source)]
75        error: std::io::Error,
76    },
77
78    #[snafu(display("Failed to collect recordbatch"))]
79    CollectRecordbatch {
80        #[snafu(implicit)]
81        location: Location,
82        source: common_recordbatch::error::Error,
83    },
84
85    #[snafu(display("Failed to start HTTP server"))]
86    StartHttp {
87        #[snafu(source)]
88        error: hyper::Error,
89    },
90
91    #[snafu(display("Failed to start gRPC server"))]
92    StartGrpc {
93        #[snafu(source)]
94        error: tonic::transport::Error,
95    },
96
97    #[snafu(display("{} server is already started", server))]
98    AlreadyStarted {
99        server: String,
100        #[snafu(implicit)]
101        location: Location,
102    },
103
104    #[snafu(display("Failed to bind address {}", addr))]
105    TcpBind {
106        addr: SocketAddr,
107        #[snafu(source)]
108        error: std::io::Error,
109    },
110
111    #[snafu(display("Failed to convert to TcpIncoming"))]
112    TcpIncoming {
113        #[snafu(source)]
114        error: Box<dyn std::error::Error + Send + Sync>,
115    },
116
117    #[snafu(display("Failed to execute query"))]
118    ExecuteQuery {
119        #[snafu(implicit)]
120        location: Location,
121        source: BoxedError,
122    },
123
124    #[snafu(display("Failed to execute plan"))]
125    ExecutePlan {
126        #[snafu(implicit)]
127        location: Location,
128        source: BoxedError,
129    },
130
131    #[snafu(display("Execute gRPC query error"))]
132    ExecuteGrpcQuery {
133        #[snafu(implicit)]
134        location: Location,
135        source: BoxedError,
136    },
137
138    #[snafu(display("Execute gRPC request error"))]
139    ExecuteGrpcRequest {
140        #[snafu(implicit)]
141        location: Location,
142        source: BoxedError,
143    },
144
145    #[snafu(display("Failed to check database validity"))]
146    CheckDatabaseValidity {
147        #[snafu(implicit)]
148        location: Location,
149        source: BoxedError,
150    },
151
152    #[snafu(display("Failed to describe statement"))]
153    DescribeStatement { source: BoxedError },
154
155    #[snafu(display("Pipeline error"))]
156    Pipeline {
157        #[snafu(source)]
158        source: pipeline::error::Error,
159        #[snafu(implicit)]
160        location: Location,
161    },
162
163    #[snafu(display("Not supported: {}", feat))]
164    NotSupported { feat: String },
165
166    #[snafu(display("Invalid request parameter: {}", reason))]
167    InvalidParameter {
168        reason: String,
169        #[snafu(implicit)]
170        location: Location,
171    },
172
173    #[snafu(display("Invalid query: {}", reason))]
174    InvalidQuery {
175        reason: String,
176        #[snafu(implicit)]
177        location: Location,
178    },
179
180    #[snafu(display("Failed to parse query"))]
181    FailedToParseQuery {
182        #[snafu(implicit)]
183        location: Location,
184        source: sql::error::Error,
185    },
186
187    #[snafu(display("Failed to parse InfluxDB line protocol"))]
188    InfluxdbLineProtocol {
189        #[snafu(implicit)]
190        location: Location,
191        #[snafu(source)]
192        error: influxdb_line_protocol::Error,
193    },
194
195    #[snafu(display("Failed to write row"))]
196    RowWriter {
197        #[snafu(implicit)]
198        location: Location,
199        source: common_grpc::error::Error,
200    },
201
202    #[snafu(display("Failed to convert time precision, name: {}", name))]
203    TimePrecision {
204        name: String,
205        #[snafu(implicit)]
206        location: Location,
207    },
208
209    #[snafu(display("Invalid OpenTSDB Json request"))]
210    InvalidOpentsdbJsonRequest {
211        #[snafu(source)]
212        error: serde_json::error::Error,
213        #[snafu(implicit)]
214        location: Location,
215    },
216
217    #[snafu(display("Failed to decode prometheus remote request"))]
218    DecodePromRemoteRequest {
219        #[snafu(implicit)]
220        location: Location,
221        #[snafu(source)]
222        error: prost::DecodeError,
223    },
224
225    #[snafu(display("Failed to decode OTLP request"))]
226    DecodeOtlpRequest {
227        #[snafu(implicit)]
228        location: Location,
229        #[snafu(source)]
230        error: prost::DecodeError,
231    },
232
233    #[snafu(display("Failed to decompress snappy prometheus remote request"))]
234    DecompressSnappyPromRemoteRequest {
235        #[snafu(implicit)]
236        location: Location,
237        #[snafu(source)]
238        error: snap::Error,
239    },
240
241    #[snafu(display("Failed to decompress zstd prometheus remote request"))]
242    DecompressZstdPromRemoteRequest {
243        #[snafu(implicit)]
244        location: Location,
245        #[snafu(source)]
246        error: std::io::Error,
247    },
248
249    #[snafu(display("Failed to send prometheus remote request"))]
250    SendPromRemoteRequest {
251        #[snafu(implicit)]
252        location: Location,
253        #[snafu(source)]
254        error: reqwest::Error,
255    },
256
257    #[snafu(display("Invalid export metrics config, msg: {}", msg))]
258    InvalidExportMetricsConfig {
259        msg: String,
260        #[snafu(implicit)]
261        location: Location,
262    },
263
264    #[snafu(display("Failed to compress prometheus remote request"))]
265    CompressPromRemoteRequest {
266        #[snafu(implicit)]
267        location: Location,
268        #[snafu(source)]
269        error: snap::Error,
270    },
271
272    #[snafu(display("Invalid prometheus remote request, msg: {}", msg))]
273    InvalidPromRemoteRequest {
274        msg: String,
275        #[snafu(implicit)]
276        location: Location,
277    },
278
279    #[snafu(display("Invalid prometheus remote read query result, msg: {}", msg))]
280    InvalidPromRemoteReadQueryResult {
281        msg: String,
282        #[snafu(implicit)]
283        location: Location,
284    },
285
286    #[snafu(display("Invalid Flight ticket"))]
287    InvalidFlightTicket {
288        #[snafu(source)]
289        error: api::DecodeError,
290        #[snafu(implicit)]
291        location: Location,
292    },
293
294    #[snafu(display("Tls is required for {}, plain connection is rejected", server))]
295    TlsRequired { server: String },
296
297    #[snafu(display("Failed to get user info"))]
298    Auth {
299        #[snafu(implicit)]
300        location: Location,
301        source: auth::error::Error,
302    },
303
304    #[snafu(display("Not found http or grpc authorization header"))]
305    NotFoundAuthHeader {},
306
307    #[snafu(display("Not found influx http authorization info"))]
308    NotFoundInfluxAuth {},
309
310    #[snafu(display("Unsupported http auth scheme, name: {}", name))]
311    UnsupportedAuthScheme { name: String },
312
313    #[snafu(display("Invalid visibility ASCII chars"))]
314    InvalidAuthHeaderInvisibleASCII {
315        #[snafu(source)]
316        error: hyper::header::ToStrError,
317        #[snafu(implicit)]
318        location: Location,
319    },
320
321    #[snafu(display("Invalid utf-8 value"))]
322    InvalidAuthHeaderInvalidUtf8Value {
323        #[snafu(source)]
324        error: FromUtf8Error,
325        #[snafu(implicit)]
326        location: Location,
327    },
328
329    #[snafu(display("Invalid http authorization header"))]
330    InvalidAuthHeader {
331        #[snafu(implicit)]
332        location: Location,
333    },
334
335    #[snafu(display("Invalid base64 value"))]
336    InvalidBase64Value {
337        #[snafu(source)]
338        error: DecodeError,
339        #[snafu(implicit)]
340        location: Location,
341    },
342
343    #[snafu(display("Invalid utf-8 value"))]
344    InvalidUtf8Value {
345        #[snafu(source)]
346        error: FromUtf8Error,
347        #[snafu(implicit)]
348        location: Location,
349    },
350
351    #[snafu(display("Invalid http header value"))]
352    InvalidHeaderValue {
353        #[snafu(source)]
354        error: InvalidHeaderValue,
355        #[snafu(implicit)]
356        location: Location,
357    },
358
359    #[snafu(display("Error accessing catalog"))]
360    Catalog {
361        source: catalog::error::Error,
362        #[snafu(implicit)]
363        location: Location,
364    },
365
366    #[snafu(display("Cannot find requested table: {}.{}.{}", catalog, schema, table))]
367    TableNotFound {
368        catalog: String,
369        schema: String,
370        table: String,
371        #[snafu(implicit)]
372        location: Location,
373    },
374
375    #[cfg(feature = "mem-prof")]
376    #[snafu(display("Failed to dump profile data"))]
377    DumpProfileData {
378        #[snafu(implicit)]
379        location: Location,
380        source: common_mem_prof::error::Error,
381    },
382
383    #[snafu(display("Invalid prepare statement: {}", err_msg))]
384    InvalidPrepareStatement {
385        err_msg: String,
386        #[snafu(implicit)]
387        location: Location,
388    },
389
390    #[snafu(display("Failed to build HTTP response"))]
391    BuildHttpResponse {
392        #[snafu(source)]
393        error: http::Error,
394        #[snafu(implicit)]
395        location: Location,
396    },
397
398    #[snafu(display("Failed to parse PromQL: {query:?}"))]
399    ParsePromQL {
400        query: Box<PromQuery>,
401        #[snafu(implicit)]
402        location: Location,
403        source: query::error::Error,
404    },
405
406    #[snafu(display("Failed to parse timestamp: {}", timestamp))]
407    ParseTimestamp {
408        timestamp: String,
409        #[snafu(implicit)]
410        location: Location,
411        #[snafu(source)]
412        error: query::error::Error,
413    },
414
415    #[snafu(display("{}", reason))]
416    UnexpectedResult {
417        reason: String,
418        #[snafu(implicit)]
419        location: Location,
420    },
421
422    // this error is used for custom error mapping
423    // please do not delete it
424    #[snafu(display("Other error"))]
425    Other {
426        source: BoxedError,
427        #[snafu(implicit)]
428        location: Location,
429    },
430
431    #[snafu(display("Failed to join task"))]
432    JoinTask {
433        #[snafu(source)]
434        error: tokio::task::JoinError,
435        #[snafu(implicit)]
436        location: Location,
437    },
438
439    #[cfg(feature = "pprof")]
440    #[snafu(display("Failed to dump pprof data"))]
441    DumpPprof { source: common_pprof::error::Error },
442
443    #[cfg(not(windows))]
444    #[snafu(display("Failed to update jemalloc metrics"))]
445    UpdateJemallocMetrics {
446        #[snafu(source)]
447        error: tikv_jemalloc_ctl::Error,
448        #[snafu(implicit)]
449        location: Location,
450    },
451
452    #[snafu(display("DataFrame operation error"))]
453    DataFrame {
454        #[snafu(source)]
455        error: datafusion::error::DataFusionError,
456        #[snafu(implicit)]
457        location: Location,
458    },
459
460    #[snafu(display("Failed to convert scalar value"))]
461    ConvertScalarValue {
462        source: datatypes::error::Error,
463        #[snafu(implicit)]
464        location: Location,
465    },
466
467    #[snafu(display("Expected type: {:?}, actual: {:?}", expected, actual))]
468    PreparedStmtTypeMismatch {
469        expected: ConcreteDataType,
470        actual: opensrv_mysql::ColumnType,
471        #[snafu(implicit)]
472        location: Location,
473    },
474
475    #[snafu(display(
476        "Column: {}, {} incompatible, expected: {}, actual: {}",
477        column_name,
478        datatype,
479        expected,
480        actual
481    ))]
482    IncompatibleSchema {
483        column_name: String,
484        datatype: String,
485        expected: i32,
486        actual: i32,
487        #[snafu(implicit)]
488        location: Location,
489    },
490
491    #[snafu(display("Failed to convert to json"))]
492    ToJson {
493        #[snafu(source)]
494        error: serde_json::error::Error,
495        #[snafu(implicit)]
496        location: Location,
497    },
498
499    #[snafu(display("Failed to parse payload as json"))]
500    ParseJson {
501        #[snafu(source)]
502        error: serde_json::error::Error,
503        #[snafu(implicit)]
504        location: Location,
505    },
506
507    #[snafu(display("Invalid Loki labels: {}", msg))]
508    InvalidLokiLabels {
509        msg: String,
510        #[snafu(implicit)]
511        location: Location,
512    },
513
514    #[snafu(display("Invalid Loki JSON request: {}", msg))]
515    InvalidLokiPayload {
516        msg: String,
517        #[snafu(implicit)]
518        location: Location,
519    },
520
521    #[snafu(display("Unsupported content type: {:?}", content_type))]
522    UnsupportedContentType {
523        content_type: ContentType,
524        #[snafu(implicit)]
525        location: Location,
526    },
527
528    #[snafu(display("Failed to decode url"))]
529    UrlDecode {
530        #[snafu(source)]
531        error: FromUtf8Error,
532        #[snafu(implicit)]
533        location: Location,
534    },
535
536    #[snafu(display("Failed to convert Mysql value, error: {}", err_msg))]
537    MysqlValueConversion {
538        err_msg: String,
539        #[snafu(implicit)]
540        location: Location,
541    },
542
543    #[snafu(display("Invalid table name"))]
544    InvalidTableName {
545        #[snafu(source)]
546        error: tonic::metadata::errors::ToStrError,
547        #[snafu(implicit)]
548        location: Location,
549    },
550
551    #[snafu(display("Failed to initialize a watcher for file {}", path))]
552    FileWatch {
553        path: String,
554        #[snafu(source)]
555        error: notify::Error,
556    },
557
558    #[snafu(display("Timestamp overflow: {}", error))]
559    TimestampOverflow {
560        error: String,
561        #[snafu(implicit)]
562        location: Location,
563    },
564
565    #[snafu(display("Unsupported json data type for tag: {} {}", key, ty))]
566    UnsupportedJsonDataTypeForTag {
567        key: String,
568        ty: String,
569        #[snafu(implicit)]
570        location: Location,
571    },
572    #[snafu(display("Convert SQL value error"))]
573    ConvertSqlValue {
574        source: datatypes::error::Error,
575        #[snafu(implicit)]
576        location: Location,
577    },
578
579    #[snafu(display("Prepare statement not found: {}", name))]
580    PrepareStatementNotFound {
581        name: String,
582        #[snafu(implicit)]
583        location: Location,
584    },
585
586    #[snafu(display("In-flight write bytes exceeded the maximum limit"))]
587    InFlightWriteBytesExceeded {
588        #[snafu(implicit)]
589        location: Location,
590    },
591
592    #[snafu(display("Invalid elasticsearch input, reason: {}", reason))]
593    InvalidElasticsearchInput {
594        reason: String,
595        #[snafu(implicit)]
596        location: Location,
597    },
598
599    #[snafu(display("Invalid Jaeger query, reason: {}", reason))]
600    InvalidJaegerQuery {
601        reason: String,
602        #[snafu(implicit)]
603        location: Location,
604    },
605
606    #[snafu(display("DataFusion error"))]
607    DataFusion {
608        #[snafu(source)]
609        error: DataFusionError,
610        #[snafu(implicit)]
611        location: Location,
612    },
613
614    #[snafu(display("Overflow while casting `{:?}` to Interval", val))]
615    DurationOverflow { val: Duration },
616
617    #[snafu(display("Failed to handle otel-arrow request, error message: {}", err_msg))]
618    HandleOtelArrowRequest {
619        err_msg: String,
620        #[snafu(implicit)]
621        location: Location,
622    },
623
624    #[snafu(display("Unknown hint: {}", hint))]
625    UnknownHint { hint: String },
626}
627
628pub type Result<T, E = Error> = std::result::Result<T, E>;
629
630impl ErrorExt for Error {
631    fn status_code(&self) -> StatusCode {
632        use Error::*;
633        match self {
634            Internal { .. }
635            | InternalIo { .. }
636            | TokioIo { .. }
637            | StartHttp { .. }
638            | StartGrpc { .. }
639            | TcpBind { .. }
640            | SendPromRemoteRequest { .. }
641            | TcpIncoming { .. }
642            | BuildHttpResponse { .. }
643            | Arrow { .. }
644            | FileWatch { .. } => StatusCode::Internal,
645
646            AddressBind { .. }
647            | AlreadyStarted { .. }
648            | InvalidPromRemoteReadQueryResult { .. } => StatusCode::IllegalState,
649
650            UnsupportedDataType { .. } => StatusCode::Unsupported,
651
652            #[cfg(not(windows))]
653            UpdateJemallocMetrics { .. } => StatusCode::Internal,
654
655            CollectRecordbatch { .. } => StatusCode::EngineExecuteQuery,
656
657            ExecuteQuery { source, .. }
658            | ExecutePlan { source, .. }
659            | ExecuteGrpcQuery { source, .. }
660            | ExecuteGrpcRequest { source, .. }
661            | CheckDatabaseValidity { source, .. } => source.status_code(),
662
663            Pipeline { source, .. } => source.status_code(),
664
665            NotSupported { .. }
666            | InvalidParameter { .. }
667            | InvalidQuery { .. }
668            | InfluxdbLineProtocol { .. }
669            | InvalidOpentsdbJsonRequest { .. }
670            | DecodePromRemoteRequest { .. }
671            | DecodeOtlpRequest { .. }
672            | CompressPromRemoteRequest { .. }
673            | DecompressSnappyPromRemoteRequest { .. }
674            | DecompressZstdPromRemoteRequest { .. }
675            | InvalidPromRemoteRequest { .. }
676            | InvalidExportMetricsConfig { .. }
677            | InvalidFlightTicket { .. }
678            | InvalidPrepareStatement { .. }
679            | DataFrame { .. }
680            | PreparedStmtTypeMismatch { .. }
681            | TimePrecision { .. }
682            | UrlDecode { .. }
683            | IncompatibleSchema { .. }
684            | MysqlValueConversion { .. }
685            | ParseJson { .. }
686            | InvalidLokiLabels { .. }
687            | InvalidLokiPayload { .. }
688            | UnsupportedContentType { .. }
689            | TimestampOverflow { .. }
690            | UnsupportedJsonDataTypeForTag { .. }
691            | InvalidTableName { .. }
692            | PrepareStatementNotFound { .. }
693            | FailedToParseQuery { .. }
694            | InvalidElasticsearchInput { .. }
695            | InvalidJaegerQuery { .. }
696            | ParseTimestamp { .. }
697            | UnknownHint { .. } => StatusCode::InvalidArguments,
698
699            Catalog { source, .. } => source.status_code(),
700            RowWriter { source, .. } => source.status_code(),
701
702            TlsRequired { .. } => StatusCode::Unknown,
703            Auth { source, .. } => source.status_code(),
704            DescribeStatement { source } => source.status_code(),
705
706            NotFoundAuthHeader { .. } | NotFoundInfluxAuth { .. } => StatusCode::AuthHeaderNotFound,
707            InvalidAuthHeaderInvisibleASCII { .. }
708            | UnsupportedAuthScheme { .. }
709            | InvalidAuthHeader { .. }
710            | InvalidBase64Value { .. }
711            | InvalidAuthHeaderInvalidUtf8Value { .. } => StatusCode::InvalidAuthHeader,
712
713            TableNotFound { .. } => StatusCode::TableNotFound,
714
715            #[cfg(feature = "mem-prof")]
716            DumpProfileData { source, .. } => source.status_code(),
717
718            InvalidUtf8Value { .. } | InvalidHeaderValue { .. } => StatusCode::InvalidArguments,
719
720            ParsePromQL { source, .. } => source.status_code(),
721            Other { source, .. } => source.status_code(),
722
723            UnexpectedResult { .. } => StatusCode::Unexpected,
724
725            JoinTask { error, .. } => {
726                if error.is_cancelled() {
727                    StatusCode::Cancelled
728                } else if error.is_panic() {
729                    StatusCode::Unexpected
730                } else {
731                    StatusCode::Unknown
732                }
733            }
734
735            #[cfg(feature = "pprof")]
736            DumpPprof { source, .. } => source.status_code(),
737
738            ConvertScalarValue { source, .. } => source.status_code(),
739
740            ToJson { .. } | DataFusion { .. } => StatusCode::Internal,
741
742            ConvertSqlValue { source, .. } => source.status_code(),
743
744            InFlightWriteBytesExceeded { .. } => StatusCode::RateLimited,
745
746            DurationOverflow { .. } => StatusCode::InvalidArguments,
747
748            HandleOtelArrowRequest { .. } => StatusCode::Internal,
749        }
750    }
751
752    fn as_any(&self) -> &dyn Any {
753        self
754    }
755}
756
757define_into_tonic_status!(Error);
758
759impl From<std::io::Error> for Error {
760    fn from(e: std::io::Error) -> Self {
761        Error::InternalIo { error: e }
762    }
763}
764
765fn log_error_if_necessary(error: &Error) {
766    if error.status_code().should_log_error() {
767        error!(error; "Failed to handle HTTP request ");
768    } else {
769        warn!(error; "Failed to handle HTTP request ");
770    }
771}
772
773impl IntoResponse for Error {
774    fn into_response(self) -> Response {
775        let error_msg = self.output_msg();
776        let status = status_code_to_http_status(&self.status_code());
777
778        log_error_if_necessary(&self);
779
780        let body = Json(json!({
781            "error": error_msg,
782        }));
783        (status, body).into_response()
784    }
785}
786
787/// Converts [StatusCode] to [HttpStatusCode].
788pub fn status_code_to_http_status(status_code: &StatusCode) -> HttpStatusCode {
789    match status_code {
790        StatusCode::Success => HttpStatusCode::OK,
791
792        // When a request is cancelled by the client (e.g., by a client side timeout),
793        // we should return a gateway timeout status code to the external client.
794        StatusCode::Cancelled | StatusCode::DeadlineExceeded => HttpStatusCode::GATEWAY_TIMEOUT,
795
796        StatusCode::Unsupported
797        | StatusCode::InvalidArguments
798        | StatusCode::InvalidSyntax
799        | StatusCode::RequestOutdated
800        | StatusCode::RegionAlreadyExists
801        | StatusCode::TableColumnExists
802        | StatusCode::TableAlreadyExists
803        | StatusCode::RegionNotFound
804        | StatusCode::DatabaseNotFound
805        | StatusCode::TableNotFound
806        | StatusCode::TableColumnNotFound
807        | StatusCode::PlanQuery
808        | StatusCode::DatabaseAlreadyExists
809        | StatusCode::FlowNotFound
810        | StatusCode::FlowAlreadyExists => HttpStatusCode::BAD_REQUEST,
811
812        StatusCode::AuthHeaderNotFound
813        | StatusCode::InvalidAuthHeader
814        | StatusCode::UserNotFound
815        | StatusCode::UnsupportedPasswordType
816        | StatusCode::UserPasswordMismatch
817        | StatusCode::RegionReadonly => HttpStatusCode::UNAUTHORIZED,
818
819        StatusCode::PermissionDenied | StatusCode::AccessDenied => HttpStatusCode::FORBIDDEN,
820
821        StatusCode::RateLimited => HttpStatusCode::TOO_MANY_REQUESTS,
822
823        StatusCode::RegionNotReady
824        | StatusCode::TableUnavailable
825        | StatusCode::RegionBusy
826        | StatusCode::StorageUnavailable
827        | StatusCode::External => HttpStatusCode::SERVICE_UNAVAILABLE,
828
829        StatusCode::Internal
830        | StatusCode::Unexpected
831        | StatusCode::IllegalState
832        | StatusCode::Unknown
833        | StatusCode::RuntimeResourcesExhausted
834        | StatusCode::EngineExecuteQuery => HttpStatusCode::INTERNAL_SERVER_ERROR,
835    }
836}