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