-
Notifications
You must be signed in to change notification settings - Fork 0
Closed
Labels
enhancementNew feature or requestNew feature or request
Description
Summary
Add request_id (from cf-ray header), request_url, timestamp, and status_code to HTTP-related exceptions and expose them via MarketDataClientErrorResult with support ticket helper methods to improve debugging and support ticket resolution.
Problem
When users report issues to support, they need to provide the cf-ray request ID for investigation. Currently:
- Users must parse log files to find the request ID
- If logging is disabled or logs are lost, the request ID is unrecoverable
- The request URL is not accessible, making it harder to identify which endpoint failed
- There's no easy way to generate a formatted support ticket with all necessary context
Proposed Solution
Scope
Only HTTP-related exceptions will carry request context. Validation exceptions that occur before a request is made cannot have this information.
| Exception | Has Request Context |
|---|---|
RateLimitError |
Yes |
BadStatusCodeError |
Yes |
RequestError |
Yes |
KeywordOnlyArgumentError |
No (validation) |
InvalidStatusDataError |
No (parsing) |
MinMaxDateValidationError |
No (validation) |
Implementation
1. Update HTTP-Related Exception Classes
Add request_id, request_url, status_code, and timestamp to HTTP exceptions:
# exceptions.py
from datetime import datetime, timezone
class RateLimitError(Exception):
"""Raised when API rate limit is exceeded."""
def __init__(
self,
message: str,
status_code: int = 0,
request_id: str | None = None,
request_url: str | None = None,
):
super().__init__(message)
self.status_code = status_code
self.request_id = request_id
self.request_url = request_url
self.timestamp = datetime.now(timezone.utc)
class BadStatusCodeError(Exception):
"""Raised when API returns an error status code."""
def __init__(
self,
message: str,
status_code: int = 0,
request_id: str | None = None,
request_url: str | None = None,
):
super().__init__(message)
self.status_code = status_code
self.request_id = request_id
self.request_url = request_url
self.timestamp = datetime.now(timezone.utc)
class RequestError(Exception):
"""Raised when a request fails after retries."""
def __init__(
self,
message: str,
status_code: int = 0,
request_id: str | None = None,
request_url: str | None = None,
):
super().__init__(message)
self.status_code = status_code
self.request_id = request_id
self.request_url = request_url
self.timestamp = datetime.now(timezone.utc)2. Add Support Ticket Helpers to MarketDataClientErrorResult
# sdk_error.py
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
class MarketDataClientErrorResult:
"""Special result type for handling errors."""
def __init__(self, error: Exception):
self.error = error
@property
def request_id(self) -> str | None:
"""The cf-ray request ID, if available."""
return getattr(self.error, "request_id", None)
@property
def request_url(self) -> str | None:
"""The request URL, if available."""
return getattr(self.error, "request_url", None)
@property
def timestamp(self) -> datetime | None:
"""The timestamp when the exception occurred (UTC), if available."""
return getattr(self.error, "timestamp", None)
@property
def status_code(self) -> int:
"""The HTTP status code, if available."""
return getattr(self.error, "status_code", 0)
def get_support_context(self) -> dict:
"""
Get all support ticket context as a dictionary.
Useful for structured logging (JSON, log aggregation systems)
or when you need to process the error details programmatically.
Example:
if isinstance(result, MarketDataClientErrorResult):
logger.error("API Error", extra=result.get_support_context())
"""
timestamp_str = None
if self.timestamp:
eastern = self.timestamp.astimezone(ZoneInfo("America/New_York"))
timestamp_str = eastern.isoformat()
return {
"timestamp": timestamp_str,
"request_id": self.request_id,
"url": self.request_url,
"http_code": self.status_code,
"message": str(self.error),
"exception_type": self.error.__class__.__name__,
}
def get_support_info(self) -> str:
"""
Get a pre-formatted string with all information needed for a support ticket.
Copy and paste this output directly into your support request at
support@marketdata.app or in the customer dashboard.
Example:
if isinstance(result, MarketDataClientErrorResult):
print(result.get_support_info())
"""
timestamp_str = "N/A"
if self.timestamp:
eastern = self.timestamp.astimezone(ZoneInfo("America/New_York"))
timestamp_str = eastern.strftime("%Y-%m-%d %H:%M:%S %Z")
lines = [
"--- MARKET DATA SUPPORT INFO ---",
f"Timestamp: {timestamp_str}",
f"Request ID: {self.request_id or 'N/A'}",
f"URL: {self.request_url or 'N/A'}",
f"HTTP Code: {self.status_code}",
f"Error: {self.error}",
"--------------------------------",
]
return "\n".join(lines)
def __repr__(self) -> str:
parts = [
f"MarketDataClientErrorResult(",
f"error={self.error.__class__.__name__}",
f", message={str(self.error)!r}",
]
if self.request_id:
parts.append(f", request_id={self.request_id!r}")
if self.request_url:
parts.append(f", request_url={self.request_url!r}")
if self.timestamp:
parts.append(f", timestamp={self.timestamp.isoformat()!r}")
parts.append(")")
return "".join(parts)
def __str__(self) -> str:
return self.__repr__()3. Update Client to Pass Context When Raising Exceptions
Update _validate_response_status_code in client.py to pass all context:
def _validate_response_status_code(
self,
response: Response,
retry_status_codes: list[int] | int | Callable,
raise_for_status: bool,
) -> None:
request_id = response.headers.get("cf-ray")
request_url = str(response.request.url)
status_code = response.status_code
# Pass status_code, request_id, request_url to all exceptions...Usage After Implementation
Quick Support Ticket Generation
result = client.stocks.quotes("INVALID_SYMBOL")
if isinstance(result, MarketDataClientErrorResult):
# Formatted block ready to paste into a support ticket
print(result.get_support_info())Output:
--- MARKET DATA SUPPORT INFO ---
Timestamp: 2026-01-25 10:30:45 EST
Request ID: 9c340f7d6be275f3-EZE
URL: https://api.marketdata.app/v1/stocks/quotes/INVALID_SYMBOL/?format=json
HTTP Code: 400
Error: Bad parameters, please check API documentation.
--------------------------------
Structured Logging
if isinstance(result, MarketDataClientErrorResult):
context = result.get_support_context()
logger.error("API request failed", extra=context)
# Or encode as JSON
print(json.dumps(context, indent=2))Output:
{
"timestamp": "2026-01-25T10:30:45-05:00",
"request_id": "9c340f7d6be275f3-EZE",
"url": "https://api.marketdata.app/v1/stocks/quotes/INVALID_SYMBOL/?format=json",
"http_code": 400,
"message": "Bad parameters, please check API documentation.",
"exception_type": "BadStatusCodeError"
}Accessing Individual Properties
if isinstance(result, MarketDataClientErrorResult):
print(f"Error: {result.error}")
print(f"Request ID: {result.request_id}")
print(f"URL: {result.request_url}")
print(f"HTTP Code: {result.status_code}")
print(f"Timestamp: {result.timestamp}")Benefits
- Faster support resolution - Users can immediately provide
request_idin tickets - Better debugging - URL shows exactly which endpoint failed
- No log parsing required - Context is programmatically accessible
- Graceful degradation - Properties return
Nonewhen context isn't available - Consistent with PHP SDK - Implements same pattern as
MarketDataExceptionhelper methods - Support-ready output -
get_support_info()provides copy-paste ready text - Structured logging support -
get_support_context()returns dict for log aggregation
Backward Compatibility
- Exception constructors accept new optional keyword arguments with
Nonedefaults - Existing
except RateLimitErrorhandlers continue to work MarketDataClientErrorResultmaintains same interface, adds read-only properties- No changes to public method signatures
Files to Modify
| File | Changes |
|---|---|
src/marketdata/exceptions.py |
Add request_id, request_url, status_code, timestamp to HTTP exceptions |
src/marketdata/sdk_error.py |
Add properties and get_support_info(), get_support_context() methods |
src/marketdata/client.py |
Pass context when raising exceptions |
src/tests/test_exceptions.py |
Add tests for new attributes |
src/tests/test_client.py |
Verify context is passed correctly |
Related
- PHP SDK:
MarketDataExceptionwithgetRequestId(),getRequestUrl(),getTimestamp(),getSupportInfo(),getSupportContext() - C# SDK:
MarketDataException.RequestIdandMarketDataException.RequestUrl
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request