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