Skip to main content

dapr_durabletask/api/
errors.rs

1use super::FailureDetails;
2
3/// Error types for the Durable Task SDK.
4#[derive(Debug, thiserror::Error)]
5pub enum DurableTaskError {
6    #[error("gRPC error: {0}")]
7    GrpcError(Box<tonic::Status>),
8
9    #[error("Orchestration '{instance_id}' failed: {message}")]
10    OrchestrationFailed {
11        instance_id: String,
12        message: String,
13        failure_details: Option<FailureDetails>,
14    },
15
16    #[error("Orchestration '{instance_id}' not found")]
17    InstanceNotFound { instance_id: String },
18
19    #[error("Task failed: {message}")]
20    TaskFailed {
21        message: String,
22        failure_details: Option<FailureDetails>,
23    },
24
25    #[error("Non-determinism error: {message}")]
26    NonDeterminism { message: String },
27
28    #[error("Orchestration state error: {message}")]
29    OrchestrationState { message: String },
30
31    #[error("Timeout waiting for orchestration")]
32    Timeout,
33
34    #[error("Serialisation error: {0}")]
35    Serialization(#[from] serde_json::Error),
36
37    #[error("{0}")]
38    Other(String),
39}
40
41impl From<tonic::Status> for DurableTaskError {
42    fn from(status: tonic::Status) -> Self {
43        DurableTaskError::GrpcError(Box::new(status))
44    }
45}
46
47/// Convenience alias for `Result<T, DurableTaskError>`.
48pub type Result<T> = std::result::Result<T, DurableTaskError>;
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn display_grpc_error() {
56        let err = DurableTaskError::GrpcError(Box::new(tonic::Status::internal("oops")));
57        let msg = err.to_string();
58        assert!(msg.starts_with("gRPC error: "));
59        assert!(msg.contains("oops"));
60    }
61
62    #[test]
63    fn display_orchestration_failed() {
64        let err = DurableTaskError::OrchestrationFailed {
65            instance_id: "abc".into(),
66            message: "boom".into(),
67            failure_details: None,
68        };
69        assert_eq!(err.to_string(), "Orchestration 'abc' failed: boom");
70    }
71
72    #[test]
73    fn display_instance_not_found() {
74        let err = DurableTaskError::InstanceNotFound {
75            instance_id: "xyz".into(),
76        };
77        assert_eq!(err.to_string(), "Orchestration 'xyz' not found");
78    }
79
80    #[test]
81    fn display_task_failed() {
82        let err = DurableTaskError::TaskFailed {
83            message: "task err".into(),
84            failure_details: None,
85        };
86        assert_eq!(err.to_string(), "Task failed: task err");
87    }
88
89    #[test]
90    fn display_timeout() {
91        assert_eq!(
92            DurableTaskError::Timeout.to_string(),
93            "Timeout waiting for orchestration"
94        );
95    }
96
97    #[test]
98    fn display_other() {
99        let err = DurableTaskError::Other("custom".into());
100        assert_eq!(err.to_string(), "custom");
101    }
102
103    #[test]
104    fn from_tonic_status() {
105        let status = tonic::Status::internal("test");
106        let err: DurableTaskError = status.into();
107        matches!(err, DurableTaskError::GrpcError(_));
108    }
109
110    #[test]
111    fn from_serde_json_error() {
112        let json_err = serde_json::from_str::<String>("not valid json").unwrap_err();
113        let err: DurableTaskError = json_err.into();
114        matches!(err, DurableTaskError::Serialization(_));
115    }
116}