Skip to main content

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