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