The Spotify SDK raises specific exceptions for different error conditions, making it easy to handle errors appropriately in your application.
Exception Hierarchy
All SDK exceptions inherit from SpotifyError, allowing you to catch all SDK errors with a single exception handler:
from spotify_sdk.exceptions import SpotifyError
try:
album = client.albums.get("invalid-id")
except SpotifyError as e:
print(f"Spotify error: {e.message}")
print(f"Status code: {e.status_code}")
print(f"Response: {e.response_body}")
SpotifyError
Base exception for all SDK errors
AuthenticationError
Invalid or expired authentication credentials (HTTP 401)
BadRequestError
Invalid request parameters (HTTP 400)
ForbiddenError
Insufficient permissions for this operation (HTTP 403)
NotFoundError
Requested resource does not exist (HTTP 404)
RateLimitError
Rate limit exceeded (HTTP 429)
ServerError
Spotify API server error (HTTP 5xx)
Exception Types
All exceptions are defined in src/spotify_sdk/exceptions.py.
SpotifyError
Base exception for all Spotify SDK errors:
class SpotifyError(Exception):
"""Base exception for all Spotify SDK errors."""
def __init__(
self,
message: str,
status_code: int | None = None,
response_body: dict[str, Any] | None = None,
) -> None:
self.message = message
self.status_code = status_code
self.response_body = response_body
super().__init__(message)
Source: src/spotify_sdk/exceptions.py:8-20
Human-readable error message
HTTP status code if the error came from an API response
Full response body from the API for debugging
AuthenticationError
Raised when authentication credentials are invalid or expired:
from spotify_sdk import SpotifyClient, AuthenticationError
client = SpotifyClient(access_token="expired-token")
try:
album = client.albums.get("4aawyAB9vmqN3uQ7FjRGTy")
except AuthenticationError as e:
print(f"Authentication failed: {e.message}")
# Re-authenticate or refresh token
HTTP Status: 401 Unauthorized
Source: src/spotify_sdk/exceptions.py:23-26
BadRequestError
Raised when request parameters are invalid:
from spotify_sdk import SpotifyClient, BadRequestError
client = SpotifyClient(access_token="your-access-token")
try:
# Invalid limit (must be 1-50)
tracks = client.albums.get_tracks("4aawyAB9vmqN3uQ7FjRGTy", limit=100)
except BadRequestError as e:
print(f"Invalid request: {e.message}")
print(f"Response: {e.response_body}")
HTTP Status: 400 Bad Request
Source: src/spotify_sdk/exceptions.py:29-32
ForbiddenError
Raised when you lack permissions for an operation:
from spotify_sdk import SpotifyClient, ForbiddenError
client = SpotifyClient.from_client_credentials(
client_id="your-client-id",
client_secret="your-client-secret",
)
try:
# Requires user authorization, not client credentials
saved_albums = client.albums.get_saved()
except ForbiddenError as e:
print(f"Insufficient permissions: {e.message}")
# Use authorization code flow instead
HTTP Status: 403 Forbidden
Source: src/spotify_sdk/exceptions.py:35-38
NotFoundError
Raised when a requested resource doesn’t exist:
from spotify_sdk import SpotifyClient, NotFoundError
client = SpotifyClient(access_token="your-access-token")
try:
album = client.albums.get("invalid-album-id")
except NotFoundError as e:
print(f"Album not found: {e.message}")
# Handle missing resource
HTTP Status: 404 Not Found
Source: src/spotify_sdk/exceptions.py:41-44
RateLimitError
Raised when you exceed Spotify’s rate limits:
class RateLimitError(SpotifyError):
"""Rate limit exceeded."""
def __init__(
self,
message: str,
status_code: int | None = None,
response_body: dict[str, Any] | None = None,
*,
retry_after: int = 0,
) -> None:
super().__init__(message, status_code, response_body)
self.retry_after = retry_after
HTTP Status: 429 Too Many Requests
Source: src/spotify_sdk/exceptions.py:47-59
Number of seconds to wait before retrying, from the Retry-After header
ServerError
Raised when Spotify’s servers encounter an error:
from spotify_sdk import SpotifyClient, ServerError
import time
client = SpotifyClient(access_token="your-access-token")
try:
album = client.albums.get("4aawyAB9vmqN3uQ7FjRGTy")
except ServerError as e:
print(f"Server error: {e.message}")
print(f"Status: {e.status_code}")
# Retry after a delay
time.sleep(5)
HTTP Status: 5xx Server Error
Source: src/spotify_sdk/exceptions.py:62-65
Error Handling Patterns
Catch All SDK Errors
Handle any SDK error with a single handler:
from spotify_sdk import SpotifyClient
from spotify_sdk.exceptions import SpotifyError
client = SpotifyClient(access_token="your-access-token")
try:
album = client.albums.get("4aawyAB9vmqN3uQ7FjRGTy")
print(f"{album.name} by {album.artists[0].name}")
except SpotifyError as e:
print(f"Error: {e.message}")
if e.status_code:
print(f"HTTP Status: {e.status_code}")
if e.response_body:
print(f"Response: {e.response_body}")
finally:
client.close()
Catch Specific Errors
Handle different error types separately:
from spotify_sdk import (
SpotifyClient,
AuthenticationError,
BadRequestError,
ForbiddenError,
NotFoundError,
RateLimitError,
ServerError,
)
client = SpotifyClient(access_token="your-access-token")
try:
album = client.albums.get(album_id)
except AuthenticationError:
print("Token expired, re-authenticating...")
# Refresh token and retry
except NotFoundError:
print(f"Album {album_id} not found")
# Try alternative or skip
except BadRequestError as e:
print(f"Invalid request: {e.message}")
# Log and fix request parameters
except ForbiddenError:
print("Insufficient permissions")
# Request additional scopes
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after} seconds")
time.sleep(e.retry_after)
# Retry request
except ServerError:
print("Spotify server error")
# Retry with exponential backoff
finally:
client.close()
Async Error Handling
Error handling works the same way with AsyncSpotifyClient:
import asyncio
from spotify_sdk import AsyncSpotifyClient, NotFoundError
async def get_album(album_id: str):
async with AsyncSpotifyClient(access_token="your-access-token") as client:
try:
album = await client.albums.get(album_id)
return album
except NotFoundError:
print(f"Album {album_id} not found")
return None
album = asyncio.run(get_album("4aawyAB9vmqN3uQ7FjRGTy"))
Rate Limit Handling
The SDK automatically retries rate-limited requests, but you may want custom handling:
import time
from spotify_sdk import SpotifyClient, RateLimitError
client = SpotifyClient(access_token="your-access-token")
def get_album_with_retry(album_id: str, max_attempts: int = 3):
for attempt in range(max_attempts):
try:
return client.albums.get(album_id)
except RateLimitError as e:
if attempt == max_attempts - 1:
raise
print(f"Rate limited. Waiting {e.retry_after} seconds...")
time.sleep(e.retry_after)
return None
try:
album = get_album_with_retry("4aawyAB9vmqN3uQ7FjRGTy")
print(album.name)
finally:
client.close()
Source: Rate limit handling in src/spotify_sdk/_async/_base_client.py:146-154
The SDK automatically:
- Retries up to 3 times (configurable with
max_retries)
- Respects the
Retry-After header
- Uses exponential backoff with jitter
Validation Errors
The SDK validates request parameters and raises ValueError for invalid inputs:
from spotify_sdk import SpotifyClient
client = SpotifyClient(access_token="your-access-token")
try:
# Empty ID is invalid
album = client.albums.get("")
except ValueError as e:
print(f"Invalid parameter: {e}")
# Output: "id cannot be empty"
try:
# Empty list is invalid
albums = client.albums.get_several([])
except ValueError as e:
print(f"Invalid parameter: {e}")
# Output: "ids cannot be empty"
Source: Example validation in src/spotify_sdk/_async/services/albums.py:25-26
Debugging Errors
Access Error Details
All exceptions provide detailed information for debugging:
from spotify_sdk import SpotifyClient
from spotify_sdk.exceptions import SpotifyError
client = SpotifyClient(access_token="your-access-token")
try:
album = client.albums.get("invalid-id")
except SpotifyError as e:
print(f"Message: {e.message}")
print(f"Status Code: {e.status_code}")
print(f"Response Body: {e.response_body}")
# Access specific error details from response
if e.response_body and "error" in e.response_body:
error_info = e.response_body["error"]
if isinstance(error_info, dict):
print(f"Error Type: {error_info.get('status')}")
print(f"Error Message: {error_info.get('message')}")
Log Errors
Integrate with Python’s logging:
import logging
from spotify_sdk import SpotifyClient
from spotify_sdk.exceptions import SpotifyError
logger = logging.getLogger(__name__)
client = SpotifyClient(access_token="your-access-token")
try:
album = client.albums.get(album_id)
except SpotifyError as e:
logger.error(
"Spotify API error",
extra={
"message": e.message,
"status_code": e.status_code,
"response_body": e.response_body,
"album_id": album_id,
},
)
raise
finally:
client.close()
Common Error Scenarios
Expired Token
from spotify_sdk import SpotifyClient, AuthenticationError
from spotify_sdk.auth import ClientCredentials
auth = ClientCredentials(
client_id="your-client-id",
client_secret="your-client-secret",
)
client = SpotifyClient(auth_provider=auth)
try:
# SDK automatically refreshes expired tokens
album = client.albums.get("4aawyAB9vmqN3uQ7FjRGTy")
except AuthenticationError as e:
# Only raised if refresh fails
print(f"Authentication failed: {e.message}")
finally:
client.close()
Invalid Resource ID
from spotify_sdk import SpotifyClient, NotFoundError, BadRequestError
client = SpotifyClient(access_token="your-access-token")
def get_album_safe(album_id: str):
try:
return client.albums.get(album_id)
except NotFoundError:
print(f"Album {album_id} does not exist")
return None
except BadRequestError:
print(f"Invalid album ID format: {album_id}")
return None
album = get_album_safe("not-a-valid-id")
Permission Denied
from spotify_sdk import SpotifyClient, ForbiddenError
from spotify_sdk.auth import AuthorizationCode
# Authorization with insufficient scopes
auth = AuthorizationCode(
client_id="your-client-id",
client_secret="your-client-secret",
redirect_uri="http://127.0.0.1:8080/callback",
scope=["user-read-private"], # Missing user-library-read
)
auth.authorize_local()
client = SpotifyClient(auth_provider=auth)
try:
saved_albums = client.albums.get_saved()
except ForbiddenError:
print("Missing required scope: user-library-read")
# Re-authorize with correct scopes
finally:
client.close()
Network Errors
import time
from spotify_sdk import SpotifyClient
from spotify_sdk.exceptions import SpotifyError
client = SpotifyClient(
access_token="your-access-token",
max_retries=5, # Increase retries for unreliable networks
timeout=60.0, # Increase timeout
)
try:
album = client.albums.get("4aawyAB9vmqN3uQ7FjRGTy")
except SpotifyError as e:
if "Connection error" in e.message:
print("Network issue, check your connection")
else:
raise
finally:
client.close()
Best Practices
Always Use Context Managers
Ensure resources are cleaned up even when errors occur:with SpotifyClient(access_token="token") as client:
album = client.albums.get("id")
# Client is closed even if an exception occurs
Catch Specific Exceptions
Handle different error types appropriately:try:
album = client.albums.get(album_id)
except NotFoundError:
return None # Resource not found
except RateLimitError as e:
time.sleep(e.retry_after) # Rate limited
# Retry
Log Error Details
Include context for debugging:except SpotifyError as e:
logger.error(f"Failed to get album {album_id}: {e.message}",
extra={"status_code": e.status_code})
Don't Ignore Errors
Handle or re-raise errors rather than silently catching:try:
album = client.albums.get(album_id)
except NotFoundError:
logger.warning(f"Album {album_id} not found")
raise # Re-raise for caller to handle