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_id in tickets
- Better debugging - URL shows exactly which endpoint failed
- No log parsing required - Context is programmatically accessible
- Graceful degradation - Properties return
None when context isn't available
- Consistent with PHP SDK - Implements same pattern as
MarketDataException helper 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
None defaults
- Existing
except RateLimitError handlers continue to work
MarketDataClientErrorResult maintains 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:
MarketDataException with getRequestId(), getRequestUrl(), getTimestamp(), getSupportInfo(), getSupportContext()
- C# SDK:
MarketDataException.RequestId and MarketDataException.RequestUrl
Summary
Add
request_id(fromcf-rayheader),request_url,timestamp, andstatus_codeto HTTP-related exceptions and expose them viaMarketDataClientErrorResultwith support ticket helper methods to improve debugging and support ticket resolution.Problem
When users report issues to support, they need to provide the
cf-rayrequest ID for investigation. Currently:Proposed Solution
Scope
Only HTTP-related exceptions will carry request context. Validation exceptions that occur before a request is made cannot have this information.
RateLimitErrorBadStatusCodeErrorRequestErrorKeywordOnlyArgumentErrorInvalidStatusDataErrorMinMaxDateValidationErrorImplementation
1. Update HTTP-Related Exception Classes
Add
request_id,request_url,status_code, andtimestampto HTTP exceptions:2. Add Support Ticket Helpers to MarketDataClientErrorResult
3. Update Client to Pass Context When Raising Exceptions
Update
_validate_response_status_codeinclient.pyto pass all context:Usage After Implementation
Quick Support Ticket Generation
Output:
Structured Logging
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
Benefits
request_idin ticketsNonewhen context isn't availableMarketDataExceptionhelper methodsget_support_info()provides copy-paste ready textget_support_context()returns dict for log aggregationBackward Compatibility
Nonedefaultsexcept RateLimitErrorhandlers continue to workMarketDataClientErrorResultmaintains same interface, adds read-only propertiesFiles to Modify
src/marketdata/exceptions.pyrequest_id,request_url,status_code,timestampto HTTP exceptionssrc/marketdata/sdk_error.pyget_support_info(),get_support_context()methodssrc/marketdata/client.pysrc/tests/test_exceptions.pysrc/tests/test_client.pyRelated
MarketDataExceptionwithgetRequestId(),getRequestUrl(),getTimestamp(),getSupportInfo(),getSupportContext()MarketDataException.RequestIdandMarketDataException.RequestUrl