0
# Error Handling
1
2
PyFCM provides comprehensive exception handling for various Firebase Cloud Messaging error conditions. The error system helps developers identify and respond appropriately to different failure scenarios including authentication issues, server problems, and data validation errors.
3
4
## Capabilities
5
6
### Base Exception Class
7
8
Root exception class for all PyFCM-specific errors, providing a common base for exception handling.
9
10
```python { .api }
11
class FCMError(Exception):
12
"""
13
Base exception class for all PyFCM errors.
14
15
All PyFCM-specific exceptions inherit from this class,
16
enabling broad exception handling with `except FCMError`.
17
"""
18
```
19
20
### Authentication Errors
21
22
Errors related to API authentication, credentials, and authorization issues.
23
24
```python { .api }
25
class AuthenticationError(FCMError):
26
"""
27
Raised when API key not found or authentication fails.
28
29
Common causes:
30
- Invalid or missing service account file
31
- Incorrect project ID
32
- Expired or invalid OAuth2 credentials
33
- Missing required authentication parameters
34
35
Resolution:
36
- Verify service account file path and contents
37
- Ensure project ID matches the service account
38
- Check credential scopes and permissions
39
"""
40
```
41
42
### Registration Token Errors
43
44
Errors related to invalid, expired, or unregistered device tokens.
45
46
```python { .api }
47
class FCMNotRegisteredError(FCMError):
48
"""
49
Raised when device token is invalid, missing, or unregistered.
50
51
Common causes:
52
- App uninstalled from device
53
- App data cleared
54
- Token expired or rotated
55
- Invalid token format
56
57
Resolution:
58
- Remove token from database
59
- Request new token from client app
60
- Implement token refresh logic
61
"""
62
```
63
64
### Sender Authorization Errors
65
66
Errors when the authenticated sender doesn't match the token's registered sender.
67
68
```python { .api }
69
class FCMSenderIdMismatchError(FCMError):
70
"""
71
Raised when authenticated sender differs from token's registered sender.
72
73
Common causes:
74
- Using wrong service account for token
75
- Token registered with different Firebase project
76
- Cross-project token usage
77
78
Resolution:
79
- Verify service account matches token's project
80
- Use correct credentials for target project
81
- Re-register token with current project
82
"""
83
```
84
85
### Server Errors
86
87
Errors indicating Firebase Cloud Messaging service issues or connectivity problems.
88
89
```python { .api }
90
class FCMServerError(FCMError):
91
"""
92
Raised for internal server errors or service timeouts.
93
94
Common causes:
95
- FCM service temporarily unavailable
96
- Network connectivity issues
97
- Server overload or maintenance
98
- Request timeout
99
100
Resolution:
101
- Implement retry logic with exponential backoff
102
- Check FCM service status
103
- Verify network connectivity
104
- Increase timeout values if needed
105
"""
106
```
107
108
### Data Validation Errors
109
110
Errors related to incorrect data format, structure, or validation failures.
111
112
```python { .api }
113
class InvalidDataError(FCMError):
114
"""
115
Raised when input data is incorrectly formatted or structured.
116
117
Common causes:
118
- Invalid JSON in configuration objects
119
- Wrong data types for parameters
120
- Missing required fields
121
- Data exceeding size limits (4KB payload limit)
122
123
Resolution:
124
- Validate input data structure
125
- Check parameter types and formats
126
- Ensure data payload within size limits
127
- Review FCM API documentation for requirements
128
"""
129
```
130
131
### Internal Package Errors
132
133
Errors indicating internal PyFCM processing issues.
134
135
```python { .api }
136
class InternalPackageError(FCMError):
137
"""
138
Raised for JSON parsing errors or internal package issues.
139
140
This error indicates a problem within PyFCM itself,
141
typically related to response parsing or internal state.
142
143
Resolution:
144
- Report issue to PyFCM maintainers
145
- Include error details and reproduction steps
146
- Check for known issues in project repository
147
"""
148
```
149
150
### Retry Control Exceptions
151
152
Exceptions for handling FCM retry-after responses and flow control.
153
154
```python { .api }
155
class RetryAfterException(Exception):
156
"""
157
Raised when FCM returns Retry-After header requiring delay.
158
159
Attributes:
160
- delay (int): Seconds to wait before retrying
161
162
Note: This exception must be handled by external logic
163
as it requires application-specific retry strategies.
164
"""
165
166
def __init__(self, delay):
167
"""
168
Initialize with retry delay.
169
170
Parameters:
171
- delay (int): Seconds to wait before retry
172
"""
173
self.delay = delay
174
```
175
176
## Usage Examples
177
178
### Basic Error Handling
179
180
```python
181
from pyfcm import FCMNotification
182
from pyfcm.errors import (
183
AuthenticationError,
184
FCMNotRegisteredError,
185
FCMServerError,
186
InvalidDataError
187
)
188
189
fcm = FCMNotification(
190
service_account_file="service-account.json",
191
project_id="your-project-id"
192
)
193
194
try:
195
result = fcm.notify(
196
fcm_token="device_token",
197
notification_title="Test Message",
198
notification_body="Testing error handling"
199
)
200
print(f"Message sent successfully: {result['name']}")
201
202
except AuthenticationError as e:
203
print(f"Authentication failed: {e}")
204
# Handle credential issues
205
206
except FCMNotRegisteredError as e:
207
print(f"Invalid token: {e}")
208
# Remove token from database
209
210
except FCMServerError as e:
211
print(f"FCM service error: {e}")
212
# Implement retry logic
213
214
except InvalidDataError as e:
215
print(f"Data validation error: {e}")
216
# Fix data format issues
217
```
218
219
### Comprehensive Error Handling
220
221
```python
222
from pyfcm.errors import FCMError
223
import time
224
import logging
225
226
def send_with_retry(fcm, **kwargs):
227
"""Send notification with retry logic and comprehensive error handling"""
228
max_attempts = 3
229
base_delay = 1
230
231
for attempt in range(max_attempts):
232
try:
233
return fcm.notify(**kwargs)
234
235
except AuthenticationError as e:
236
logging.error(f"Authentication error (not retryable): {e}")
237
raise # Don't retry authentication errors
238
239
except FCMNotRegisteredError as e:
240
logging.warning(f"Token unregistered: {e}")
241
# Remove token from database
242
return None
243
244
except FCMServerError as e:
245
if attempt < max_attempts - 1:
246
delay = base_delay * (2 ** attempt)
247
logging.warning(f"Server error, retrying in {delay}s: {e}")
248
time.sleep(delay)
249
continue
250
else:
251
logging.error(f"Server error after {max_attempts} attempts: {e}")
252
raise
253
254
except InvalidDataError as e:
255
logging.error(f"Data validation error (not retryable): {e}")
256
raise # Don't retry data errors
257
258
except FCMError as e:
259
logging.error(f"Unknown FCM error: {e}")
260
raise
261
```
262
263
### Batch Operation Error Handling
264
265
```python
266
def handle_batch_responses(responses, tokens):
267
"""Process batch operation responses and handle errors"""
268
successful = []
269
failed_tokens = []
270
271
for i, (response, token) in enumerate(zip(responses, tokens)):
272
try:
273
if 'error' in response:
274
error_code = response['error'].get('status', 'UNKNOWN')
275
276
if error_code == 'NOT_FOUND':
277
# Token no longer valid
278
failed_tokens.append(token)
279
logging.warning(f"Token {i} invalid, removing: {token}")
280
281
elif error_code == 'INVALID_ARGUMENT':
282
logging.error(f"Token {i} data error: {response['error']}")
283
284
else:
285
logging.error(f"Token {i} unknown error: {response['error']}")
286
else:
287
successful.append(response['name'])
288
289
except KeyError as e:
290
logging.error(f"Unexpected response format for token {i}: {e}")
291
292
return successful, failed_tokens
293
```
294
295
### Token Management with Error Handling
296
297
```python
298
class TokenManager:
299
"""Manage FCM tokens with automatic error handling and cleanup"""
300
301
def __init__(self, fcm_client):
302
self.fcm = fcm_client
303
self.invalid_tokens = set()
304
305
def send_to_token(self, token, **kwargs):
306
"""Send message with automatic token validation"""
307
if token in self.invalid_tokens:
308
return None
309
310
try:
311
return self.fcm.notify(fcm_token=token, **kwargs)
312
313
except FCMNotRegisteredError:
314
# Mark token as invalid and remove from storage
315
self.invalid_tokens.add(token)
316
self.remove_token_from_database(token)
317
return None
318
319
except FCMSenderIdMismatchError:
320
# Token belongs to different project
321
self.invalid_tokens.add(token)
322
return None
323
324
def remove_token_from_database(self, token):
325
"""Remove invalid token from persistent storage"""
326
# Implementation depends on storage system
327
pass
328
```
329
330
## Error Response Codes
331
332
PyFCM maps HTTP status codes to appropriate exceptions:
333
334
- **200**: Success
335
- **400**: InvalidDataError - Malformed request
336
- **401**: AuthenticationError - Invalid credentials
337
- **403**: FCMSenderIdMismatchError - Sender not authorized for token
338
- **404**: FCMNotRegisteredError - Token not found/registered
339
- **500+**: FCMServerError - Server errors and timeouts
340
341
## Best Practices
342
343
### Retry Strategies
344
345
- **Don't retry**: AuthenticationError, InvalidDataError, FCMSenderIdMismatchError
346
- **Retry with backoff**: FCMServerError
347
- **Remove and don't retry**: FCMNotRegisteredError
348
349
### Logging and Monitoring
350
351
```python
352
import logging
353
354
# Configure logging for error tracking
355
logging.basicConfig(
356
level=logging.INFO,
357
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
358
)
359
360
# Log all FCM operations for monitoring
361
logger = logging.getLogger('pyfcm_operations')
362
```
363
364
### Token Lifecycle Management
365
366
1. **Registration**: Store tokens securely with metadata
367
2. **Validation**: Handle registration errors during messaging
368
3. **Refresh**: Implement client-side token refresh logic
369
4. **Cleanup**: Remove invalid tokens promptly