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("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 #[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
960pub fn status_code_to_http_status(status_code: &StatusCode) -> HttpStatusCode {
962 match status_code {
963 StatusCode::Success => HttpStatusCode::OK,
964
965 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}