Headroom

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 failed
import {
  ,
  ,
  ,
  ,
  ,
  ,
  ,
} 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 failed
from 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 response
try:
    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 HeadroomMode

ProviderError

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 metrics

CompressionError

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 StatusProxy Error TypeTypeScript Class
401--HeadroomAuthError
4xx/5xxconfiguration_errorConfigurationError
4xx/5xxprovider_errorProviderError
4xx/5xxstorage_errorStorageError
4xx/5xxtokenization_errorTokenizationError
4xx/5xxcache_errorCacheError
4xx/5xxvalidation_errorValidationError
4xx/5xxtransform_errorTransformError
4xx/5xx(other)HeadroomCompressError

The mapProxyError() function handles this mapping:

import {  } from 'headroom-ai';

const  = (400, 'configuration_error', 'Invalid mode');
// Returns a ConfigurationError instance

Error 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 .errorType
try:
    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 exception

Safety 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

  1. Catch specific exceptions rather than broad Exception to avoid hiding real bugs
  2. 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)
  3. Validate on startup with client.validate_setup() to catch configuration issues early
  4. 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.

On this page