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