A newer version of the Gradio SDK is available:
6.1.0
Exception Hierarchy
Last Updated: 2025-12-06
This document describes all custom exceptions in DeepBoner.
Location
All exceptions are defined in src/utils/exceptions.py.
Exception Tree
Exception (Python builtin)
βββ DeepBonerError (base)
βββ SearchError
β βββ RateLimitError
βββ JudgeError
βββ ConfigurationError
βββ EmbeddingError
βββ LLMError
β βββ QuotaExceededError
βββ SynthesisError
Base Exception
DeepBonerError
class DeepBonerError(Exception):
"""Base exception for all DeepBoner errors."""
pass
When to use: Never directly. Use specific subclasses.
Catch when: You want to catch all DeepBoner-related errors.
try:
result = orchestrator.run(query)
except DeepBonerError as e:
logger.error(f"Research failed: {e}")
Search Exceptions
SearchError
class SearchError(DeepBonerError):
"""Raised when a search operation fails."""
pass
When raised:
- External API returns error status
- Network timeout
- Invalid response format
- No results found (in strict mode)
Example:
from src.utils.exceptions import SearchError
if response.status_code != 200:
raise SearchError(f"PubMed returned {response.status_code}")
RateLimitError
class RateLimitError(SearchError):
"""Raised when we hit API rate limits."""
pass
When raised:
- HTTP 429 (Too Many Requests)
- PubMed rate limit exceeded
- ClinicalTrials.gov throttling
Handling:
from src.utils.exceptions import RateLimitError
try:
results = pubmed.search(query)
except RateLimitError:
await asyncio.sleep(60) # Wait and retry
results = pubmed.search(query)
Prevention:
- Add
NCBI_API_KEYfor higher PubMed limits - Use built-in rate limiter (
src/tools/rate_limiter.py)
Judge Exceptions
JudgeError
class JudgeError(DeepBonerError):
"""Raised when the judge fails to assess evidence."""
pass
When raised:
- LLM fails to produce valid assessment
- Assessment parsing fails
- Confidence below threshold
- Invalid judge response format
Example:
from src.utils.exceptions import JudgeError
if not assessment.details:
raise JudgeError("Judge produced incomplete assessment")
Configuration Exceptions
ConfigurationError
class ConfigurationError(DeepBonerError):
"""Raised when configuration is invalid."""
pass
When raised:
- Required API key missing
- Invalid setting value
- Environment variable malformed
- Conflicting configuration
Example:
from src.utils.exceptions import ConfigurationError
def get_api_key(self) -> str:
if not self.openai_api_key:
raise ConfigurationError("OPENAI_API_KEY not set")
return self.openai_api_key
Embedding Exceptions
EmbeddingError
class EmbeddingError(DeepBonerError):
"""Raised when embedding or vector store operations fail."""
pass
When raised:
- ChromaDB connection failure
- Sentence-transformers model load failure
- Vector dimension mismatch
- Embedding generation fails
Example:
from src.utils.exceptions import EmbeddingError
try:
embeddings = model.encode(texts)
except Exception as e:
raise EmbeddingError(f"Embedding failed: {e}")
LLM Exceptions
LLMError
class LLMError(DeepBonerError):
"""Raised when LLM operations fail (API errors, parsing errors, etc.)."""
pass
When raised:
- LLM API error
- Response parsing failure
- Invalid model output
- Context length exceeded
QuotaExceededError
class QuotaExceededError(LLMError):
"""Raised when LLM API quota is exceeded (402 errors)."""
pass
When raised:
- OpenAI billing limit hit
- HuggingFace rate limit exceeded
- HTTP 402 Payment Required
Handling:
from src.utils.exceptions import QuotaExceededError
try:
response = client.chat_completion(messages)
except QuotaExceededError:
# Fall back to free tier or notify user
return fallback_response()
Synthesis Exceptions
SynthesisError
class SynthesisError(DeepBonerError):
"""Raised when report synthesis fails after trying all available models.
Attributes:
message: Human-readable error description
attempted_models: List of model IDs that were tried
errors: List of error messages from each failed attempt
"""
def __init__(
self,
message: str,
attempted_models: list[str] | None = None,
errors: list[str] | None = None,
) -> None:
super().__init__(message)
self.attempted_models = attempted_models or []
self.errors = errors or []
When raised:
- All LLM models fail to synthesize report
- Report generation exceeds retry limit
Example:
from src.utils.exceptions import SynthesisError
if all_attempts_failed:
raise SynthesisError(
"Failed to synthesize report",
attempted_models=["gpt-5", "gpt-4o"],
errors=["Rate limit", "Context too long"]
)
Accessing details:
try:
report = synthesize(evidence)
except SynthesisError as e:
print(f"Failed: {e}")
print(f"Tried models: {e.attempted_models}")
print(f"Errors: {e.errors}")
Usage Patterns
Catching Specific Exceptions
from src.utils.exceptions import (
SearchError,
RateLimitError,
JudgeError,
)
try:
result = orchestrator.run(query)
except RateLimitError:
# Specific handling for rate limits
await rate_limiter.wait()
result = orchestrator.run(query)
except SearchError:
# General search failure
return empty_result()
except JudgeError:
# Judge failed, use default assessment
return default_assessment()
Exception Chaining
try:
response = api_call()
except requests.RequestException as e:
raise SearchError(f"API call failed: {e}") from e
Logging Exceptions
import structlog
logger = structlog.get_logger()
try:
results = search(query)
except DeepBonerError as e:
logger.error("operation_failed", error=str(e), exc_info=True)
raise
Best Practices
- Use specific exceptions - Don't raise
DeepBonerErrordirectly - Include context - Error messages should explain what failed
- Chain exceptions - Use
from eto preserve stack trace - Log before re-raising - Capture context for debugging
- Handle at boundaries - Catch exceptions at API/UI boundaries