Error Handling
How to catch and handle Headroom errors in Python and TypeScript. Error hierarchy, proxy error mapping, and safety guarantees.
Headroom provides explicit exceptions for debugging, with a core safety guarantee: compression failures never break your LLM calls. If compression fails, the original content passes through unchanged.
Error Hierarchy
HeadroomError (base class)
+-- HeadroomConnectionError # Cannot reach proxy
+-- HeadroomAuthError # 401 from proxy
+-- HeadroomCompressError # Compression failed (with statusCode)
+-- ConfigurationError # Invalid configuration
+-- ProviderError # Provider issues
+-- StorageError # Storage failures
+-- TokenizationError # Token counting failed
+-- CacheError # Cache operations failed
+-- ValidationError # Validation failures
+-- TransformError # Transform execution failedimport {
,
,
,
,
,
,
,
} from 'headroom-ai';HeadroomError (base class)
+-- ConfigurationError # Invalid configuration
+-- ProviderError # Provider issues (unknown model, etc.)
+-- StorageError # Database/storage failures
+-- CompressionError # Compression failures (rare)
+-- TokenizationError # Token counting failed
+-- CacheError # Cache operations failed
+-- ValidationError # Setup validation failures
+-- TransformError # Transform execution failedfrom headroom import (
HeadroomError,
ConfigurationError,
ProviderError,
StorageError,
CompressionError,
TokenizationError,
CacheError,
ValidationError,
TransformError,
)Catching Errors
import { , , , , } from 'headroom-ai';
try {
const = await (messages, { : 'gpt-4o' });
} catch () {
if ( instanceof ) {
.('Cannot reach proxy:', .message);
} else if ( instanceof ) {
.('Auth failed:', .message);
} else if ( instanceof ) {
.(`Compress failed (${.statusCode}):`, .message);
} else if ( instanceof ) {
.('Headroom error:', .message, .details);
}
}from headroom import (
HeadroomClient,
HeadroomError,
ConfigurationError,
StorageError,
)
try:
client = HeadroomClient(...)
response = client.chat.completions.create(...)
except ConfigurationError as e:
print(f"Config issue: {e}")
print(f"Details: {e.details}")
except StorageError as e:
print(f"Storage issue: {e}")
# Headroom continues to work, just without metrics persistence
except HeadroomError as e:
print(f"Headroom error: {e}")Error Types in Detail
ConfigurationError
Raised when configuration is invalid. In the Python SDK (0.37.0),
ConfigurationError is importable and exported from headroom, but
HeadroomClient itself does not currently raise it: an invalid
default_mode string is validated by the HeadroomMode enum and raises a
plain ValueError, not ConfigurationError.
import { } from 'headroom-ai';
// ConfigurationError is thrown when the proxy returns
// a configuration_error type in its error responsetry:
client = HeadroomClient(
original_client=OpenAI(),
provider=OpenAIProvider(),
default_mode="invalid_mode",
)
except ValueError as e:
print(f"Invalid mode: {e}")
# 'invalid_mode' is not a valid HeadroomModeProviderError
Reserved for provider-specific issues, but not currently raised anywhere in
the SDK. In particular, an unknown model name does not raise
ProviderError: Provider.get_context_limit() is documented to "never
raise an exception -- uses sensible defaults for unknown models," and only
logs a one-time WARNING naming the fallback it used.
import logging
logging.basicConfig(level=logging.WARNING)
# Does NOT raise ProviderError -- falls back to a default context limit
# and logs: WARNING:headroom.providers.openai:Unknown OpenAI model
# 'unknown-model-xyz': using default limit (...). To configure explicitly,
# set HEADROOM_MODEL_LIMITS env var or add to ~/.headroom/models.json
response = client.chat.completions.create(
model="unknown-model-xyz",
messages=[...],
)StorageError
Reserved for database/storage failures. HeadroomClient.get_metrics() and
get_summary() currently delegate straight to the storage backend
(headroom/client.py) without wrapping failures in StorageError, so a
broken database today surfaces as the underlying driver's own exception
(for example sqlite3.OperationalError) rather than StorageError. Catch
the base HeadroomError -- or Exception around storage calls specifically
-- until that wrapping lands.
try:
metrics = client.get_metrics()
except Exception as e:
metrics = [] # Continue without historical metricsCompressionError
Reserved for compression failures. As of 0.37.0 this class is exported but
not raised anywhere in the codebase -- there is no "strict mode" that
triggers it. Compression failures are instead caught internally by broad
except Exception handlers throughout headroom/transforms/, which fail
open and return the original, uncompressed content. This is what backs the
safety guarantee below.
HeadroomConnectionError (TypeScript)
Raised when the TypeScript SDK cannot connect to the Headroom proxy.
import { , } from 'headroom-ai';
try {
await (messages, { : 'gpt-4o' });
} catch () {
if ( instanceof ) {
.('Is the proxy running? Start with: headroom proxy');
}
}Proxy Error Mapping
The TypeScript SDK automatically maps proxy error responses to the correct error class:
| HTTP Status | Proxy Error Type | TypeScript Class |
|---|---|---|
| 401 | -- | HeadroomAuthError |
| 4xx/5xx | configuration_error | ConfigurationError |
| 4xx/5xx | provider_error | ProviderError |
| 4xx/5xx | storage_error | StorageError |
| 4xx/5xx | tokenization_error | TokenizationError |
| 4xx/5xx | cache_error | CacheError |
| 4xx/5xx | validation_error | ValidationError |
| 4xx/5xx | transform_error | TransformError |
| 4xx/5xx | (other) | HeadroomCompressError |
The mapProxyError() function handles this mapping:
import { } from 'headroom-ai';
const = (400, 'configuration_error', 'Invalid mode');
// Returns a ConfigurationError instanceError Details
All Headroom exceptions include a details dict/object with additional context:
import { } from 'headroom-ai';
// HeadroomError.details is Record<string, any> | undefined
// HeadroomCompressError also has .statusCode and .errorTypetry:
client = HeadroomClient(...)
except HeadroomError as e:
print(f"Error: {e}")
print(f"Type: {type(e).__name__}")
print(f"Details: {e.details}")
# Details might include:
# - field: which config field caused the error
# - provider: which provider was involved
# - model: which model was requested
# - original_error: underlying exceptionSafety Guarantee
If compression fails, the original content passes through unchanged. Your LLM calls never fail due to Headroom:
messages = [
{"role": "tool", "content": "malformed json {{{"}
]
# This will NOT raise an exception
# The malformed content passes through unchanged
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
)Best Practices
- Catch specific exceptions rather than broad
Exceptionto avoid hiding real bugs - Don't rely on storage failures raising -- as of 0.37.0 they surface as the
underlying driver's own exception, not
StorageError(see above) - Validate on startup with
client.validate_setup()to catch configuration issues early - Enable logging at WARNING level to see when compression falls back safely
import logging
logging.basicConfig(level=logging.WARNING)
# WARNING:headroom.transforms.smart_crusher:SmartCrusher audit_safe: 1 protected
# row(s) could not be preserved through compression (...). Failing closed:
# returning original uncompressed.