Python client for FCM - Firebase Cloud Messaging (Android, iOS and Web)
—
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.
Root exception class for all PyFCM-specific errors, providing a common base for exception handling.
class FCMError(Exception):
"""
Base exception class for all PyFCM errors.
All PyFCM-specific exceptions inherit from this class,
enabling broad exception handling with `except FCMError`.
"""Errors related to API authentication, credentials, and authorization issues.
class AuthenticationError(FCMError):
"""
Raised when API key not found or authentication fails.
Common causes:
- Invalid or missing service account file
- Incorrect project ID
- Expired or invalid OAuth2 credentials
- Missing required authentication parameters
Resolution:
- Verify service account file path and contents
- Ensure project ID matches the service account
- Check credential scopes and permissions
"""Errors related to invalid, expired, or unregistered device tokens.
class FCMNotRegisteredError(FCMError):
"""
Raised when device token is invalid, missing, or unregistered.
Common causes:
- App uninstalled from device
- App data cleared
- Token expired or rotated
- Invalid token format
Resolution:
- Remove token from database
- Request new token from client app
- Implement token refresh logic
"""Errors when the authenticated sender doesn't match the token's registered sender.
class FCMSenderIdMismatchError(FCMError):
"""
Raised when authenticated sender differs from token's registered sender.
Common causes:
- Using wrong service account for token
- Token registered with different Firebase project
- Cross-project token usage
Resolution:
- Verify service account matches token's project
- Use correct credentials for target project
- Re-register token with current project
"""Errors indicating Firebase Cloud Messaging service issues or connectivity problems.
class FCMServerError(FCMError):
"""
Raised for internal server errors or service timeouts.
Common causes:
- FCM service temporarily unavailable
- Network connectivity issues
- Server overload or maintenance
- Request timeout
Resolution:
- Implement retry logic with exponential backoff
- Check FCM service status
- Verify network connectivity
- Increase timeout values if needed
"""Errors related to incorrect data format, structure, or validation failures.
class InvalidDataError(FCMError):
"""
Raised when input data is incorrectly formatted or structured.
Common causes:
- Invalid JSON in configuration objects
- Wrong data types for parameters
- Missing required fields
- Data exceeding size limits (4KB payload limit)
Resolution:
- Validate input data structure
- Check parameter types and formats
- Ensure data payload within size limits
- Review FCM API documentation for requirements
"""Errors indicating internal PyFCM processing issues.
class InternalPackageError(FCMError):
"""
Raised for JSON parsing errors or internal package issues.
This error indicates a problem within PyFCM itself,
typically related to response parsing or internal state.
Resolution:
- Report issue to PyFCM maintainers
- Include error details and reproduction steps
- Check for known issues in project repository
"""Exceptions for handling FCM retry-after responses and flow control.
class RetryAfterException(Exception):
"""
Raised when FCM returns Retry-After header requiring delay.
Attributes:
- delay (int): Seconds to wait before retrying
Note: This exception must be handled by external logic
as it requires application-specific retry strategies.
"""
def __init__(self, delay):
"""
Initialize with retry delay.
Parameters:
- delay (int): Seconds to wait before retry
"""
self.delay = delayfrom pyfcm import FCMNotification
from pyfcm.errors import (
AuthenticationError,
FCMNotRegisteredError,
FCMServerError,
InvalidDataError
)
fcm = FCMNotification(
service_account_file="service-account.json",
project_id="your-project-id"
)
try:
result = fcm.notify(
fcm_token="device_token",
notification_title="Test Message",
notification_body="Testing error handling"
)
print(f"Message sent successfully: {result['name']}")
except AuthenticationError as e:
print(f"Authentication failed: {e}")
# Handle credential issues
except FCMNotRegisteredError as e:
print(f"Invalid token: {e}")
# Remove token from database
except FCMServerError as e:
print(f"FCM service error: {e}")
# Implement retry logic
except InvalidDataError as e:
print(f"Data validation error: {e}")
# Fix data format issuesfrom pyfcm.errors import FCMError
import time
import logging
def send_with_retry(fcm, **kwargs):
"""Send notification with retry logic and comprehensive error handling"""
max_attempts = 3
base_delay = 1
for attempt in range(max_attempts):
try:
return fcm.notify(**kwargs)
except AuthenticationError as e:
logging.error(f"Authentication error (not retryable): {e}")
raise # Don't retry authentication errors
except FCMNotRegisteredError as e:
logging.warning(f"Token unregistered: {e}")
# Remove token from database
return None
except FCMServerError as e:
if attempt < max_attempts - 1:
delay = base_delay * (2 ** attempt)
logging.warning(f"Server error, retrying in {delay}s: {e}")
time.sleep(delay)
continue
else:
logging.error(f"Server error after {max_attempts} attempts: {e}")
raise
except InvalidDataError as e:
logging.error(f"Data validation error (not retryable): {e}")
raise # Don't retry data errors
except FCMError as e:
logging.error(f"Unknown FCM error: {e}")
raisedef handle_batch_responses(responses, tokens):
"""Process batch operation responses and handle errors"""
successful = []
failed_tokens = []
for i, (response, token) in enumerate(zip(responses, tokens)):
try:
if 'error' in response:
error_code = response['error'].get('status', 'UNKNOWN')
if error_code == 'NOT_FOUND':
# Token no longer valid
failed_tokens.append(token)
logging.warning(f"Token {i} invalid, removing: {token}")
elif error_code == 'INVALID_ARGUMENT':
logging.error(f"Token {i} data error: {response['error']}")
else:
logging.error(f"Token {i} unknown error: {response['error']}")
else:
successful.append(response['name'])
except KeyError as e:
logging.error(f"Unexpected response format for token {i}: {e}")
return successful, failed_tokensclass TokenManager:
"""Manage FCM tokens with automatic error handling and cleanup"""
def __init__(self, fcm_client):
self.fcm = fcm_client
self.invalid_tokens = set()
def send_to_token(self, token, **kwargs):
"""Send message with automatic token validation"""
if token in self.invalid_tokens:
return None
try:
return self.fcm.notify(fcm_token=token, **kwargs)
except FCMNotRegisteredError:
# Mark token as invalid and remove from storage
self.invalid_tokens.add(token)
self.remove_token_from_database(token)
return None
except FCMSenderIdMismatchError:
# Token belongs to different project
self.invalid_tokens.add(token)
return None
def remove_token_from_database(self, token):
"""Remove invalid token from persistent storage"""
# Implementation depends on storage system
passPyFCM maps HTTP status codes to appropriate exceptions:
import logging
# Configure logging for error tracking
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Log all FCM operations for monitoring
logger = logging.getLogger('pyfcm_operations')Install with Tessl CLI
npx tessl i tessl/pypi-pyfcm