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