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::{Json, http};
22use base64::DecodeError;
23use common_base::readable_size::ReadableSize;
24use common_error::define_into_tonic_status;
25use common_error::ext::{BoxedError, ErrorExt};
26use common_error::status_code::StatusCode;
27use common_macro::stack_trace_debug;
28use common_telemetry::{error, warn};
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("Request memory limit exceeded"))]
98    MemoryLimitExceeded {
99        #[snafu(implicit)]
100        location: Location,
101        source: common_memory_manager::Error,
102    },
103
104    #[snafu(display("{} server is already started", server))]
105    AlreadyStarted {
106        server: String,
107        #[snafu(implicit)]
108        location: Location,
109    },
110
111    #[snafu(display("Failed to bind address {}", addr))]
112    TcpBind {
113        addr: SocketAddr,
114        #[snafu(source)]
115        error: std::io::Error,
116    },
117
118    #[snafu(display("Failed to execute query"))]
119    ExecuteQuery {
120        #[snafu(implicit)]
121        location: Location,
122        source: BoxedError,
123    },
124
125    #[snafu(display("Failed to execute plan"))]
126    ExecutePlan {
127        #[snafu(implicit)]
128        location: Location,
129        source: BoxedError,
130    },
131
132    #[snafu(display("Execute gRPC query error"))]
133    ExecuteGrpcQuery {
134        #[snafu(implicit)]
135        location: Location,
136        source: BoxedError,
137    },
138
139    #[snafu(display("Execute gRPC request error"))]
140    ExecuteGrpcRequest {
141        #[snafu(implicit)]
142        location: Location,
143        source: BoxedError,
144    },
145
146    #[snafu(display("Failed to check database validity"))]
147    CheckDatabaseValidity {
148        #[snafu(implicit)]
149        location: Location,
150        source: BoxedError,
151    },
152
153    #[snafu(display("Failed to describe statement"))]
154    DescribeStatement { source: BoxedError },
155
156    #[snafu(display("Pipeline error"))]
157    Pipeline {
158        #[snafu(source)]
159        source: pipeline::error::Error,
160        #[snafu(implicit)]
161        location: Location,
162    },
163
164    #[snafu(display("Not supported: {}", feat))]
165    NotSupported { feat: String },
166
167    #[snafu(display("Invalid request parameter: {}", reason))]
168    InvalidParameter {
169        reason: String,
170        #[snafu(implicit)]
171        location: Location,
172    },
173
174    #[snafu(display(
175        "Too many concurrent large requests, limit: {}, request size: {}",
176        ReadableSize(*limit as u64),
177        ReadableSize(*request_size as u64)
178    ))]
179    TooManyConcurrentRequests {
180        limit: usize,
181        request_size: usize,
182        #[snafu(implicit)]
183        location: Location,
184    },
185
186    #[snafu(display("Invalid query: {}", reason))]
187    InvalidQuery {
188        reason: String,
189        #[snafu(implicit)]
190        location: Location,
191    },
192
193    #[snafu(display("Failed to parse query"))]
194    FailedToParseQuery {
195        #[snafu(implicit)]
196        location: Location,
197        source: sql::error::Error,
198    },
199
200    #[snafu(display("Failed to parse InfluxDB line protocol"))]
201    InfluxdbLineProtocol {
202        #[snafu(implicit)]
203        location: Location,
204        #[snafu(source)]
205        error: influxdb_line_protocol::Error,
206    },
207
208    #[snafu(display("Failed to write row"))]
209    RowWriter {
210        #[snafu(implicit)]
211        location: Location,
212        source: common_grpc::error::Error,
213    },
214
215    #[snafu(display("Failed to convert time precision, name: {}", name))]
216    TimePrecision {
217        name: String,
218        #[snafu(implicit)]
219        location: Location,
220    },
221
222    #[snafu(display("Invalid OpenTSDB Json request"))]
223    InvalidOpentsdbJsonRequest {
224        #[snafu(source)]
225        error: serde_json::error::Error,
226        #[snafu(implicit)]
227        location: Location,
228    },
229
230    #[snafu(display("Failed to decode prometheus remote request"))]
231    DecodePromRemoteRequest {
232        #[snafu(implicit)]
233        location: Location,
234        #[snafu(source)]
235        error: prost::DecodeError,
236    },
237
238    #[snafu(display(
239        "Failed to decode OTLP request (content-type: {content_type}): {error}. The endpoint only accepts 'application/x-protobuf' format."
240    ))]
241    DecodeOtlpRequest {
242        content_type: String,
243        #[snafu(implicit)]
244        location: Location,
245        #[snafu(source)]
246        error: prost::DecodeError,
247    },
248
249    #[snafu(display("Failed to decode Loki request: {error}"))]
250    DecodeLokiRequest {
251        #[snafu(implicit)]
252        location: Location,
253        #[snafu(source)]
254        error: prost::DecodeError,
255    },
256
257    #[snafu(display(
258        "Unsupported content type 'application/json'. OTLP endpoint only supports 'application/x-protobuf'. Please configure your OTLP exporter to use protobuf encoding."
259    ))]
260    UnsupportedJsonContentType {
261        #[snafu(implicit)]
262        location: Location,
263    },
264
265    #[snafu(display(
266        "OTLP metric input have incompatible existing tables, please refer to docs for details"
267    ))]
268    OtlpMetricModeIncompatible {
269        #[snafu(implicit)]
270        location: Location,
271    },
272
273    #[snafu(display("Common Meta error"))]
274    CommonMeta {
275        #[snafu(implicit)]
276        location: Location,
277        #[snafu(source)]
278        source: common_meta::error::Error,
279    },
280
281    #[snafu(display("Failed to decompress snappy prometheus remote request"))]
282    DecompressSnappyPromRemoteRequest {
283        #[snafu(implicit)]
284        location: Location,
285        #[snafu(source)]
286        error: snap::Error,
287    },
288
289    #[snafu(display("Failed to decompress zstd prometheus remote request"))]
290    DecompressZstdPromRemoteRequest {
291        #[snafu(implicit)]
292        location: Location,
293        #[snafu(source)]
294        error: std::io::Error,
295    },
296
297    #[snafu(display("Failed to compress prometheus remote request"))]
298    CompressPromRemoteRequest {
299        #[snafu(implicit)]
300        location: Location,
301        #[snafu(source)]
302        error: snap::Error,
303    },
304
305    #[snafu(display("Invalid prometheus remote request, msg: {}", msg))]
306    InvalidPromRemoteRequest {
307        msg: String,
308        #[snafu(implicit)]
309        location: Location,
310    },
311
312    #[snafu(display("Invalid prometheus remote read query result, msg: {}", msg))]
313    InvalidPromRemoteReadQueryResult {
314        msg: String,
315        #[snafu(implicit)]
316        location: Location,
317    },
318
319    #[snafu(display("Invalid Flight ticket"))]
320    InvalidFlightTicket {
321        #[snafu(source)]
322        error: api::DecodeError,
323        #[snafu(implicit)]
324        location: Location,
325    },
326
327    #[snafu(display("Tls is required for {}, plain connection is rejected", server))]
328    TlsRequired { server: String },
329
330    #[snafu(display("Failed to get user info"))]
331    Auth {
332        #[snafu(implicit)]
333        location: Location,
334        source: auth::error::Error,
335    },
336
337    #[snafu(display("Not found http or grpc authorization header"))]
338    NotFoundAuthHeader {},
339
340    #[snafu(display("Not found influx http authorization info"))]
341    NotFoundInfluxAuth {},
342
343    #[snafu(display("Unsupported http auth scheme, name: {}", name))]
344    UnsupportedAuthScheme { name: String },
345
346    #[snafu(display("Invalid visibility ASCII chars"))]
347    InvalidAuthHeaderInvisibleASCII {
348        #[snafu(source)]
349        error: hyper::header::ToStrError,
350        #[snafu(implicit)]
351        location: Location,
352    },
353
354    #[snafu(display("Invalid utf-8 value"))]
355    InvalidAuthHeaderInvalidUtf8Value {
356        #[snafu(source)]
357        error: FromUtf8Error,
358        #[snafu(implicit)]
359        location: Location,
360    },
361
362    #[snafu(display("Invalid http authorization header"))]
363    InvalidAuthHeader {
364        #[snafu(implicit)]
365        location: Location,
366    },
367
368    #[snafu(display("Invalid base64 value"))]
369    InvalidBase64Value {
370        #[snafu(source)]
371        error: DecodeError,
372        #[snafu(implicit)]
373        location: Location,
374    },
375
376    #[snafu(display("Invalid utf-8 value"))]
377    InvalidUtf8Value {
378        #[snafu(source)]
379        error: FromUtf8Error,
380        #[snafu(implicit)]
381        location: Location,
382    },
383
384    #[snafu(display("Invalid http header value"))]
385    InvalidHeaderValue {
386        #[snafu(source)]
387        error: InvalidHeaderValue,
388        #[snafu(implicit)]
389        location: Location,
390    },
391
392    #[snafu(display("Error accessing catalog"))]
393    Catalog {
394        source: catalog::error::Error,
395        #[snafu(implicit)]
396        location: Location,
397    },
398
399    #[snafu(display("Cannot find requested table: {}.{}.{}", catalog, schema, table))]
400    TableNotFound {
401        catalog: String,
402        schema: String,
403        table: String,
404        #[snafu(implicit)]
405        location: Location,
406    },
407
408    #[cfg(feature = "mem-prof")]
409    #[snafu(display("Failed to dump profile data"))]
410    DumpProfileData {
411        #[snafu(implicit)]
412        location: Location,
413        source: common_mem_prof::error::Error,
414    },
415
416    #[snafu(display("Invalid prepare statement: {}", err_msg))]
417    InvalidPrepareStatement {
418        err_msg: String,
419        #[snafu(implicit)]
420        location: Location,
421    },
422
423    #[snafu(display("Failed to build HTTP response"))]
424    BuildHttpResponse {
425        #[snafu(source)]
426        error: http::Error,
427        #[snafu(implicit)]
428        location: Location,
429    },
430
431    #[snafu(display("Failed to parse PromQL: {query:?}"))]
432    ParsePromQL {
433        query: Box<PromQuery>,
434        #[snafu(implicit)]
435        location: Location,
436        source: query::error::Error,
437    },
438
439    #[snafu(display("Failed to parse timestamp: {}", timestamp))]
440    ParseTimestamp {
441        timestamp: String,
442        #[snafu(implicit)]
443        location: Location,
444        #[snafu(source)]
445        error: query::error::Error,
446    },
447
448    #[snafu(display("{}", reason))]
449    UnexpectedResult {
450        reason: String,
451        #[snafu(implicit)]
452        location: Location,
453    },
454
455    // this error is used for custom error mapping
456    // please do not delete it
457    #[snafu(display("Other error"))]
458    Other {
459        source: BoxedError,
460        #[snafu(implicit)]
461        location: Location,
462    },
463
464    #[snafu(display("Failed to join task"))]
465    JoinTask {
466        #[snafu(source)]
467        error: tokio::task::JoinError,
468        #[snafu(implicit)]
469        location: Location,
470    },
471
472    #[cfg(feature = "pprof")]
473    #[snafu(display("Failed to dump pprof data"))]
474    DumpPprof { source: common_pprof::error::Error },
475
476    #[cfg(not(windows))]
477    #[snafu(display("Failed to update jemalloc metrics"))]
478    UpdateJemallocMetrics {
479        #[snafu(source)]
480        error: tikv_jemalloc_ctl::Error,
481        #[snafu(implicit)]
482        location: Location,
483    },
484
485    #[snafu(display("DataFrame operation error"))]
486    DataFrame {
487        #[snafu(source)]
488        error: datafusion::error::DataFusionError,
489        #[snafu(implicit)]
490        location: Location,
491    },
492
493    #[snafu(display("Failed to convert scalar value"))]
494    ConvertScalarValue {
495        source: datatypes::error::Error,
496        #[snafu(implicit)]
497        location: Location,
498    },
499
500    #[snafu(display("Expected type: {:?}, actual: {:?}", expected, actual))]
501    PreparedStmtTypeMismatch {
502        expected: ConcreteDataType,
503        actual: opensrv_mysql::ColumnType,
504        #[snafu(implicit)]
505        location: Location,
506    },
507
508    #[snafu(display(
509        "Column: {}, {} incompatible, expected: {}, actual: {}",
510        column_name,
511        datatype,
512        expected,
513        actual
514    ))]
515    IncompatibleSchema {
516        column_name: String,
517        datatype: String,
518        expected: i32,
519        actual: i32,
520        #[snafu(implicit)]
521        location: Location,
522    },
523
524    #[snafu(display("Failed to convert to json"))]
525    ToJson {
526        #[snafu(source)]
527        error: serde_json::error::Error,
528        #[snafu(implicit)]
529        location: Location,
530    },
531
532    #[snafu(display("Failed to parse payload as json"))]
533    ParseJson {
534        #[snafu(source)]
535        error: serde_json::error::Error,
536        #[snafu(implicit)]
537        location: Location,
538    },
539
540    #[snafu(display("Invalid Loki labels: {}", msg))]
541    InvalidLokiLabels {
542        msg: String,
543        #[snafu(implicit)]
544        location: Location,
545    },
546
547    #[snafu(display("Invalid Loki JSON request: {}", msg))]
548    InvalidLokiPayload {
549        msg: String,
550        #[snafu(implicit)]
551        location: Location,
552    },
553
554    #[snafu(display("Unsupported content type: {:?}", content_type))]
555    UnsupportedContentType {
556        content_type: ContentType,
557        #[snafu(implicit)]
558        location: Location,
559    },
560
561    #[snafu(display("Failed to decode url"))]
562    UrlDecode {
563        #[snafu(source)]
564        error: FromUtf8Error,
565        #[snafu(implicit)]
566        location: Location,
567    },
568
569    #[snafu(display("Failed to convert Mysql value, error: {}", err_msg))]
570    MysqlValueConversion {
571        err_msg: String,
572        #[snafu(implicit)]
573        location: Location,
574    },
575
576    #[snafu(display("Invalid table name"))]
577    InvalidTableName {
578        #[snafu(source)]
579        error: tonic::metadata::errors::ToStrError,
580        #[snafu(implicit)]
581        location: Location,
582    },
583
584    #[snafu(display("Failed to initialize a watcher for file {}", path))]
585    FileWatch {
586        path: String,
587        #[snafu(source)]
588        error: notify::Error,
589    },
590
591    #[snafu(display("Timestamp overflow: {}", error))]
592    TimestampOverflow {
593        error: String,
594        #[snafu(implicit)]
595        location: Location,
596    },
597
598    #[snafu(display("Unsupported json data type for tag: {} {}", key, ty))]
599    UnsupportedJsonDataTypeForTag {
600        key: String,
601        ty: String,
602        #[snafu(implicit)]
603        location: Location,
604    },
605
606    #[snafu(display("Convert SQL value error"))]
607    ConvertSqlValue {
608        source: datatypes::error::Error,
609        #[snafu(implicit)]
610        location: Location,
611    },
612
613    #[snafu(display("Prepare statement not found: {}", name))]
614    PrepareStatementNotFound {
615        name: String,
616        #[snafu(implicit)]
617        location: Location,
618    },
619
620    #[snafu(display("Invalid elasticsearch input, reason: {}", reason))]
621    InvalidElasticsearchInput {
622        reason: String,
623        #[snafu(implicit)]
624        location: Location,
625    },
626
627    #[snafu(display("Invalid Jaeger query, reason: {}", reason))]
628    InvalidJaegerQuery {
629        reason: String,
630        #[snafu(implicit)]
631        location: Location,
632    },
633
634    #[snafu(display("DataFusion error"))]
635    DataFusion {
636        #[snafu(source)]
637        error: DataFusionError,
638        #[snafu(implicit)]
639        location: Location,
640    },
641
642    #[snafu(display("Failed to handle otel-arrow request, error message: {}", err_msg))]
643    HandleOtelArrowRequest {
644        err_msg: String,
645        #[snafu(implicit)]
646        location: Location,
647    },
648
649    #[snafu(display("Unknown hint: {}", hint))]
650    UnknownHint { hint: String },
651
652    #[snafu(display("Query has been cancelled"))]
653    Cancelled {
654        #[snafu(implicit)]
655        location: Location,
656    },
657
658    #[snafu(display("Service suspended"))]
659    Suspended {
660        #[snafu(implicit)]
661        location: Location,
662    },
663
664    #[snafu(transparent)]
665    GreptimeProto {
666        source: api::error::Error,
667        #[snafu(implicit)]
668        location: Location,
669    },
670}
671
672pub type Result<T, E = Error> = std::result::Result<T, E>;
673
674impl ErrorExt for Error {
675    fn status_code(&self) -> StatusCode {
676        use Error::*;
677        match self {
678            Internal { .. }
679            | InternalIo { .. }
680            | TokioIo { .. }
681            | StartHttp { .. }
682            | StartGrpc { .. }
683            | TcpBind { .. }
684            | BuildHttpResponse { .. }
685            | Arrow { .. }
686            | FileWatch { .. } => StatusCode::Internal,
687
688            AddressBind { .. }
689            | AlreadyStarted { .. }
690            | InvalidPromRemoteReadQueryResult { .. }
691            | OtlpMetricModeIncompatible { .. } => StatusCode::IllegalState,
692
693            UnsupportedDataType { .. } => StatusCode::Unsupported,
694
695            #[cfg(not(windows))]
696            UpdateJemallocMetrics { .. } => StatusCode::Internal,
697
698            CollectRecordbatch { .. } => StatusCode::EngineExecuteQuery,
699
700            ExecuteQuery { source, .. }
701            | ExecutePlan { source, .. }
702            | ExecuteGrpcQuery { source, .. }
703            | ExecuteGrpcRequest { source, .. }
704            | CheckDatabaseValidity { source, .. } => source.status_code(),
705
706            Pipeline { source, .. } => source.status_code(),
707            CommonMeta { source, .. } => source.status_code(),
708
709            NotSupported { .. }
710            | InvalidParameter { .. }
711            | InvalidQuery { .. }
712            | InfluxdbLineProtocol { .. }
713            | InvalidOpentsdbJsonRequest { .. }
714            | DecodePromRemoteRequest { .. }
715            | DecodeOtlpRequest { .. }
716            | DecodeLokiRequest { .. }
717            | UnsupportedJsonContentType { .. }
718            | CompressPromRemoteRequest { .. }
719            | DecompressSnappyPromRemoteRequest { .. }
720            | DecompressZstdPromRemoteRequest { .. }
721            | InvalidPromRemoteRequest { .. }
722            | InvalidFlightTicket { .. }
723            | InvalidPrepareStatement { .. }
724            | DataFrame { .. }
725            | PreparedStmtTypeMismatch { .. }
726            | TimePrecision { .. }
727            | UrlDecode { .. }
728            | IncompatibleSchema { .. }
729            | MysqlValueConversion { .. }
730            | ParseJson { .. }
731            | InvalidLokiLabels { .. }
732            | InvalidLokiPayload { .. }
733            | UnsupportedContentType { .. }
734            | TimestampOverflow { .. }
735            | UnsupportedJsonDataTypeForTag { .. }
736            | InvalidTableName { .. }
737            | PrepareStatementNotFound { .. }
738            | FailedToParseQuery { .. }
739            | InvalidElasticsearchInput { .. }
740            | InvalidJaegerQuery { .. }
741            | ParseTimestamp { .. }
742            | UnknownHint { .. } => StatusCode::InvalidArguments,
743
744            Catalog { source, .. } => source.status_code(),
745            RowWriter { source, .. } => source.status_code(),
746
747            TlsRequired { .. } => StatusCode::Unknown,
748            Auth { source, .. } => source.status_code(),
749            DescribeStatement { source } => source.status_code(),
750
751            NotFoundAuthHeader { .. } | NotFoundInfluxAuth { .. } => StatusCode::AuthHeaderNotFound,
752            InvalidAuthHeaderInvisibleASCII { .. }
753            | UnsupportedAuthScheme { .. }
754            | InvalidAuthHeader { .. }
755            | InvalidBase64Value { .. }
756            | InvalidAuthHeaderInvalidUtf8Value { .. } => StatusCode::InvalidAuthHeader,
757
758            TableNotFound { .. } => StatusCode::TableNotFound,
759
760            #[cfg(feature = "mem-prof")]
761            DumpProfileData { source, .. } => source.status_code(),
762
763            InvalidUtf8Value { .. } | InvalidHeaderValue { .. } => StatusCode::InvalidArguments,
764
765            TooManyConcurrentRequests { .. } => StatusCode::RuntimeResourcesExhausted,
766
767            ParsePromQL { source, .. } => source.status_code(),
768            Other { source, .. } => source.status_code(),
769
770            UnexpectedResult { .. } => StatusCode::Unexpected,
771
772            JoinTask { error, .. } => {
773                if error.is_cancelled() {
774                    StatusCode::Cancelled
775                } else if error.is_panic() {
776                    StatusCode::Unexpected
777                } else {
778                    StatusCode::Unknown
779                }
780            }
781
782            #[cfg(feature = "pprof")]
783            DumpPprof { source, .. } => source.status_code(),
784
785            ConvertScalarValue { source, .. } => source.status_code(),
786
787            ToJson { .. } | DataFusion { .. } => StatusCode::Internal,
788
789            ConvertSqlValue { source, .. } => source.status_code(),
790
791            HandleOtelArrowRequest { .. } => StatusCode::Internal,
792
793            Cancelled { .. } => StatusCode::Cancelled,
794
795            Suspended { .. } => StatusCode::Suspended,
796
797            MemoryLimitExceeded { .. } => StatusCode::RateLimited,
798
799            GreptimeProto { source, .. } => source.status_code(),
800        }
801    }
802
803    fn as_any(&self) -> &dyn Any {
804        self
805    }
806}
807
808define_into_tonic_status!(Error);
809
810impl From<std::io::Error> for Error {
811    fn from(e: std::io::Error) -> Self {
812        Error::InternalIo { error: e }
813    }
814}
815
816fn log_error_if_necessary(error: &Error) {
817    if error.status_code().should_log_error() {
818        error!(error; "Failed to handle HTTP request ");
819    } else {
820        warn!(error; "Failed to handle HTTP request ");
821    }
822}
823
824impl IntoResponse for Error {
825    fn into_response(self) -> Response {
826        let error_msg = self.output_msg();
827        let status = status_code_to_http_status(&self.status_code());
828
829        log_error_if_necessary(&self);
830
831        let body = Json(json!({
832            "error": error_msg,
833        }));
834        (status, body).into_response()
835    }
836}
837
838/// Converts [StatusCode] to [HttpStatusCode].
839pub fn status_code_to_http_status(status_code: &StatusCode) -> HttpStatusCode {
840    match status_code {
841        StatusCode::Success => HttpStatusCode::OK,
842
843        // When a request is cancelled by the client (e.g., by a client side timeout),
844        // we should return a gateway timeout status code to the external client.
845        StatusCode::Cancelled | StatusCode::DeadlineExceeded => HttpStatusCode::GATEWAY_TIMEOUT,
846
847        StatusCode::Unsupported
848        | StatusCode::InvalidArguments
849        | StatusCode::InvalidSyntax
850        | StatusCode::RequestOutdated
851        | StatusCode::RegionAlreadyExists
852        | StatusCode::TableColumnExists
853        | StatusCode::TableAlreadyExists
854        | StatusCode::RegionNotFound
855        | StatusCode::DatabaseNotFound
856        | StatusCode::TableNotFound
857        | StatusCode::TableColumnNotFound
858        | StatusCode::PlanQuery
859        | StatusCode::DatabaseAlreadyExists
860        | StatusCode::TriggerAlreadyExists
861        | StatusCode::TriggerNotFound
862        | StatusCode::FlowNotFound
863        | StatusCode::FlowAlreadyExists => HttpStatusCode::BAD_REQUEST,
864
865        StatusCode::AuthHeaderNotFound
866        | StatusCode::InvalidAuthHeader
867        | StatusCode::UserNotFound
868        | StatusCode::UnsupportedPasswordType
869        | StatusCode::UserPasswordMismatch
870        | StatusCode::RegionReadonly => HttpStatusCode::UNAUTHORIZED,
871
872        StatusCode::PermissionDenied | StatusCode::AccessDenied => HttpStatusCode::FORBIDDEN,
873
874        StatusCode::RateLimited => HttpStatusCode::TOO_MANY_REQUESTS,
875
876        StatusCode::RegionNotReady
877        | StatusCode::TableUnavailable
878        | StatusCode::RegionBusy
879        | StatusCode::StorageUnavailable
880        | StatusCode::External
881        | StatusCode::Suspended => HttpStatusCode::SERVICE_UNAVAILABLE,
882
883        StatusCode::Internal
884        | StatusCode::Unexpected
885        | StatusCode::IllegalState
886        | StatusCode::Unknown
887        | StatusCode::RuntimeResourcesExhausted
888        | StatusCode::EngineExecuteQuery => HttpStatusCode::INTERNAL_SERVER_ERROR,
889    }
890}