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
669pub type Result<T, E = Error> = std::result::Result<T, E>;
670
671impl ErrorExt for Error {
672    fn status_code(&self) -> StatusCode {
673        use Error::*;
674        match self {
675            Internal { .. }
676            | InternalIo { .. }
677            | TokioIo { .. }
678            | StartHttp { .. }
679            | StartGrpc { .. }
680            | TcpBind { .. }
681            | BuildHttpResponse { .. }
682            | Arrow { .. }
683            | FileWatch { .. } => StatusCode::Internal,
684
685            AddressBind { .. }
686            | AlreadyStarted { .. }
687            | InvalidPromRemoteReadQueryResult { .. }
688            | OtlpMetricModeIncompatible { .. } => StatusCode::IllegalState,
689
690            UnsupportedDataType { .. } => StatusCode::Unsupported,
691
692            #[cfg(not(windows))]
693            UpdateJemallocMetrics { .. } => StatusCode::Internal,
694
695            CollectRecordbatch { .. } => StatusCode::EngineExecuteQuery,
696
697            ExecuteQuery { source, .. }
698            | ExecutePlan { source, .. }
699            | ExecuteGrpcQuery { source, .. }
700            | ExecuteGrpcRequest { source, .. }
701            | CheckDatabaseValidity { source, .. } => source.status_code(),
702
703            Pipeline { source, .. } => source.status_code(),
704            CommonMeta { source, .. } => source.status_code(),
705
706            NotSupported { .. }
707            | InvalidParameter { .. }
708            | InvalidQuery { .. }
709            | InfluxdbLineProtocol { .. }
710            | InvalidOpentsdbJsonRequest { .. }
711            | DecodePromRemoteRequest { .. }
712            | DecodeOtlpRequest { .. }
713            | DecodeLokiRequest { .. }
714            | UnsupportedJsonContentType { .. }
715            | CompressPromRemoteRequest { .. }
716            | DecompressSnappyPromRemoteRequest { .. }
717            | DecompressZstdPromRemoteRequest { .. }
718            | InvalidPromRemoteRequest { .. }
719            | InvalidFlightTicket { .. }
720            | InvalidPrepareStatement { .. }
721            | DataFrame { .. }
722            | PreparedStmtTypeMismatch { .. }
723            | TimePrecision { .. }
724            | UrlDecode { .. }
725            | IncompatibleSchema { .. }
726            | MysqlValueConversion { .. }
727            | ParseJson { .. }
728            | InvalidLokiLabels { .. }
729            | InvalidLokiPayload { .. }
730            | UnsupportedContentType { .. }
731            | TimestampOverflow { .. }
732            | UnsupportedJsonDataTypeForTag { .. }
733            | InvalidTableName { .. }
734            | PrepareStatementNotFound { .. }
735            | FailedToParseQuery { .. }
736            | InvalidElasticsearchInput { .. }
737            | InvalidJaegerQuery { .. }
738            | ParseTimestamp { .. }
739            | UnknownHint { .. } => StatusCode::InvalidArguments,
740
741            Catalog { source, .. } => source.status_code(),
742            RowWriter { source, .. } => source.status_code(),
743
744            TlsRequired { .. } => StatusCode::Unknown,
745            Auth { source, .. } => source.status_code(),
746            DescribeStatement { source } => source.status_code(),
747
748            NotFoundAuthHeader { .. } | NotFoundInfluxAuth { .. } => StatusCode::AuthHeaderNotFound,
749            InvalidAuthHeaderInvisibleASCII { .. }
750            | UnsupportedAuthScheme { .. }
751            | InvalidAuthHeader { .. }
752            | InvalidBase64Value { .. }
753            | InvalidAuthHeaderInvalidUtf8Value { .. } => StatusCode::InvalidAuthHeader,
754
755            TableNotFound { .. } => StatusCode::TableNotFound,
756
757            #[cfg(feature = "mem-prof")]
758            DumpProfileData { source, .. } => source.status_code(),
759
760            InvalidUtf8Value { .. } | InvalidHeaderValue { .. } => StatusCode::InvalidArguments,
761
762            TooManyConcurrentRequests { .. } => StatusCode::RuntimeResourcesExhausted,
763
764            ParsePromQL { source, .. } => source.status_code(),
765            Other { source, .. } => source.status_code(),
766
767            UnexpectedResult { .. } => StatusCode::Unexpected,
768
769            JoinTask { error, .. } => {
770                if error.is_cancelled() {
771                    StatusCode::Cancelled
772                } else if error.is_panic() {
773                    StatusCode::Unexpected
774                } else {
775                    StatusCode::Unknown
776                }
777            }
778
779            #[cfg(feature = "pprof")]
780            DumpPprof { source, .. } => source.status_code(),
781
782            ConvertScalarValue { source, .. } => source.status_code(),
783
784            ToJson { .. } | DataFusion { .. } => StatusCode::Internal,
785
786            ConvertSqlValue { source, .. } => source.status_code(),
787
788            DurationOverflow { .. } => StatusCode::InvalidArguments,
789
790            HandleOtelArrowRequest { .. } => StatusCode::Internal,
791
792            Cancelled { .. } => StatusCode::Cancelled,
793
794            Suspended { .. } => StatusCode::Suspended,
795
796            MemoryLimitExceeded { .. } => StatusCode::RateLimited,
797        }
798    }
799
800    fn as_any(&self) -> &dyn Any {
801        self
802    }
803}
804
805define_into_tonic_status!(Error);
806
807impl From<std::io::Error> for Error {
808    fn from(e: std::io::Error) -> Self {
809        Error::InternalIo { error: e }
810    }
811}
812
813fn log_error_if_necessary(error: &Error) {
814    if error.status_code().should_log_error() {
815        error!(error; "Failed to handle HTTP request ");
816    } else {
817        warn!(error; "Failed to handle HTTP request ");
818    }
819}
820
821impl IntoResponse for Error {
822    fn into_response(self) -> Response {
823        let error_msg = self.output_msg();
824        let status = status_code_to_http_status(&self.status_code());
825
826        log_error_if_necessary(&self);
827
828        let body = Json(json!({
829            "error": error_msg,
830        }));
831        (status, body).into_response()
832    }
833}
834
835/// Converts [StatusCode] to [HttpStatusCode].
836pub fn status_code_to_http_status(status_code: &StatusCode) -> HttpStatusCode {
837    match status_code {
838        StatusCode::Success => HttpStatusCode::OK,
839
840        // When a request is cancelled by the client (e.g., by a client side timeout),
841        // we should return a gateway timeout status code to the external client.
842        StatusCode::Cancelled | StatusCode::DeadlineExceeded => HttpStatusCode::GATEWAY_TIMEOUT,
843
844        StatusCode::Unsupported
845        | StatusCode::InvalidArguments
846        | StatusCode::InvalidSyntax
847        | StatusCode::RequestOutdated
848        | StatusCode::RegionAlreadyExists
849        | StatusCode::TableColumnExists
850        | StatusCode::TableAlreadyExists
851        | StatusCode::RegionNotFound
852        | StatusCode::DatabaseNotFound
853        | StatusCode::TableNotFound
854        | StatusCode::TableColumnNotFound
855        | StatusCode::PlanQuery
856        | StatusCode::DatabaseAlreadyExists
857        | StatusCode::TriggerAlreadyExists
858        | StatusCode::TriggerNotFound
859        | StatusCode::FlowNotFound
860        | StatusCode::FlowAlreadyExists => HttpStatusCode::BAD_REQUEST,
861
862        StatusCode::AuthHeaderNotFound
863        | StatusCode::InvalidAuthHeader
864        | StatusCode::UserNotFound
865        | StatusCode::UnsupportedPasswordType
866        | StatusCode::UserPasswordMismatch
867        | StatusCode::RegionReadonly => HttpStatusCode::UNAUTHORIZED,
868
869        StatusCode::PermissionDenied | StatusCode::AccessDenied => HttpStatusCode::FORBIDDEN,
870
871        StatusCode::RateLimited => HttpStatusCode::TOO_MANY_REQUESTS,
872
873        StatusCode::RegionNotReady
874        | StatusCode::TableUnavailable
875        | StatusCode::RegionBusy
876        | StatusCode::StorageUnavailable
877        | StatusCode::External
878        | StatusCode::Suspended => HttpStatusCode::SERVICE_UNAVAILABLE,
879
880        StatusCode::Internal
881        | StatusCode::Unexpected
882        | StatusCode::IllegalState
883        | StatusCode::Unknown
884        | StatusCode::RuntimeResourcesExhausted
885        | StatusCode::EngineExecuteQuery => HttpStatusCode::INTERNAL_SERVER_ERROR,
886    }
887}