telemetry_kit/error.rs
1//! Error types for telemetry-kit
2//!
3//! All errors include helpful context and actionable suggestions where possible.
4
5use thiserror::Error;
6
7/// Result type for telemetry operations
8pub type Result<T> = std::result::Result<T, TelemetryError>;
9
10/// Errors that can occur during telemetry operations
11#[derive(Debug, Error)]
12pub enum TelemetryError {
13 /// SQLite database error
14 ///
15 /// Common causes:
16 /// - Database file is locked (another process using it)
17 /// - Insufficient permissions to write to database file
18 /// - Disk full or quota exceeded
19 /// - Corrupted database file
20 ///
21 /// Suggestions:
22 /// - Check file permissions on the database directory
23 /// - Ensure no other process is using the database
24 /// - Try deleting the database file to recreate it (data will be lost)
25 #[error("Database error: {0}\n\nSuggestion: Check file permissions and ensure the database isn't locked by another process")]
26 Database(#[from] rusqlite::Error),
27
28 /// HTTP request error
29 ///
30 /// Common causes:
31 /// - Network connectivity issues
32 /// - Invalid endpoint URL
33 /// - Server is down or unreachable
34 /// - DNS resolution failure
35 /// - SSL/TLS certificate issues
36 ///
37 /// Suggestions:
38 /// - Check your internet connection
39 /// - Verify the endpoint URL is correct
40 /// - Check if the server is accessible via curl/ping
41 #[cfg(feature = "sync")]
42 #[error("HTTP request failed: {0}\n\nSuggestion: Check network connectivity and verify the endpoint URL")]
43 Http(#[from] reqwest::Error),
44
45 /// JSON serialization/deserialization error
46 ///
47 /// Common causes:
48 /// - Invalid JSON structure
49 /// - Schema version mismatch
50 /// - Missing required fields
51 /// - Type conversion errors
52 ///
53 /// Suggestions:
54 /// - Check that SDK and server versions are compatible
55 /// - Verify custom event data is valid JSON
56 #[error("JSON serialization error: {0}\n\nSuggestion: Ensure event data is valid JSON and SDK version matches server")]
57 Json(#[from] serde_json::Error),
58
59 /// Invalid configuration
60 ///
61 /// Suggestions:
62 /// - Review the configuration documentation
63 /// - Ensure all required fields are provided
64 /// - Check that UUIDs are in valid format
65 #[error("Invalid configuration: {0}\n\nSuggestion: Review configuration requirements in the documentation")]
66 InvalidConfig(String),
67
68 /// Authentication error
69 ///
70 /// Common causes:
71 /// - Invalid or expired token
72 /// - Incorrect HMAC secret
73 /// - Token doesn't have required permissions
74 ///
75 /// Suggestions:
76 /// - Verify your token and secret are correct
77 /// - Generate a new token if the current one is expired
78 /// - Check that the token has sync permissions
79 #[error("Authentication failed: {0}\n\nSuggestion: Verify your token and secret are correct")]
80 Auth(String),
81
82 /// Rate limit exceeded
83 ///
84 /// You've exceeded the rate limit for your tier.
85 ///
86 /// Suggestions:
87 /// - Wait the specified duration before retrying
88 /// - Reduce event frequency
89 /// - Batch events together
90 /// - Consider upgrading to a higher tier
91 #[error("Rate limit exceeded. Retry after {retry_after} seconds.\n\nSuggestion: Batch events together or upgrade your plan")]
92 RateLimitExceeded {
93 /// Seconds to wait before retrying
94 retry_after: u64,
95 },
96
97 /// Server error with status code
98 ///
99 /// The server encountered an error processing your request.
100 ///
101 /// Suggestions:
102 /// - If 5xx error, retry the request after a delay
103 /// - If 4xx error, check your request parameters
104 /// - Check server status page if available
105 #[error("Server error ({status}): {message}\n\nSuggestion: {}", Self::server_error_suggestion(*status))]
106 ServerError {
107 /// HTTP status code
108 status: u16,
109 /// Error message from server
110 message: String,
111 },
112
113 /// Maximum retries exceeded
114 ///
115 /// The operation failed after multiple retry attempts.
116 ///
117 /// Common causes:
118 /// - Persistent network issues
119 /// - Server is down
120 /// - Invalid credentials
121 ///
122 /// Suggestions:
123 /// - Check server health and network connectivity
124 /// - Verify authentication credentials
125 /// - Enable offline mode to queue events locally
126 #[error("Maximum retries exceeded\n\nSuggestion: Check server health and network connectivity, or enable offline mode")]
127 MaxRetriesExceeded,
128
129 /// Invalid event schema
130 ///
131 /// The event structure doesn't match the expected schema.
132 ///
133 /// Suggestions:
134 /// - Ensure SDK version is compatible with server
135 /// - Check that required event fields are present
136 /// - Review event schema documentation
137 #[error(
138 "Invalid event schema: {0}\n\nSuggestion: Ensure SDK version is compatible with server"
139 )]
140 InvalidSchema(String),
141
142 /// IO error
143 ///
144 /// Common causes:
145 /// - File or directory doesn't exist
146 /// - Insufficient permissions
147 /// - Disk full
148 ///
149 /// Suggestions:
150 /// - Check file/directory exists and is accessible
151 /// - Verify write permissions
152 /// - Ensure sufficient disk space
153 #[error("IO error: {0}\n\nSuggestion: Check file permissions and available disk space")]
154 Io(#[from] std::io::Error),
155
156 /// Machine ID error
157 ///
158 /// Failed to generate or retrieve a unique machine identifier.
159 ///
160 /// Common causes:
161 /// - System doesn't have machine UUID available
162 /// - Insufficient permissions to access machine ID
163 ///
164 /// Suggestions:
165 /// - This is usually safe to ignore (fallback ID will be used)
166 /// - On Docker/CI, this is expected behavior
167 #[error("Failed to get machine ID: {0}\n\nNote: This is normal in Docker/CI environments. A fallback ID will be used.")]
168 MachineId(String),
169
170 /// Generic error
171 #[error("{0}")]
172 Other(String),
173}
174
175impl TelemetryError {
176 /// Check if the error is retryable
177 pub fn is_retryable(&self) -> bool {
178 match self {
179 TelemetryError::RateLimitExceeded { .. } => true,
180 TelemetryError::ServerError { status, .. } if *status >= 500 => true,
181 _ => false,
182 }
183 }
184
185 /// Get actionable suggestion based on HTTP status code
186 fn server_error_suggestion(status: u16) -> &'static str {
187 match status {
188 400 => "Check request parameters and ensure they're valid",
189 401 => "Verify your authentication token and secret",
190 403 => "Your token doesn't have permission for this operation",
191 404 => "Check the endpoint URL - resource not found",
192 413 => "Request payload too large - reduce batch size",
193 429 => "Rate limited - wait before retrying or upgrade plan",
194 500..=599 => "Server error - retry with exponential backoff",
195 _ => "Check server logs for details",
196 }
197 }
198
199 /// Create a helpful InvalidConfig error with context
200 pub fn invalid_config(field: &str, reason: &str) -> Self {
201 Self::InvalidConfig(format!("{}: {}", field, reason))
202 }
203
204 /// Create a helpful InvalidConfig error for UUID validation
205 pub fn invalid_uuid(field: &str, value: &str) -> Self {
206 Self::InvalidConfig(format!(
207 "{} '{}' is not a valid UUID. Expected format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
208 field, value
209 ))
210 }
211
212 /// Create a helpful error for missing required fields
213 pub fn missing_field(field: &str) -> Self {
214 Self::InvalidConfig(format!("Missing required field: {}", field))
215 }
216}