title: “API Error Codes & Handling” version: 1.1.1 date: 2026-01-23
API Error Codes & Handling
Comprehensive documentation for BranchPy’s error codes, exception hierarchy, and error handling patterns.
Version: 1.1.1
Last Updated: January 23, 2026
Exception Hierarchy
All BranchPy exceptions inherit from BranchPyError:
BranchPyError (base exception)
├── ParseError
│ ├── SyntaxError
│ ├── InvalidPythonCodeError
│ └── MalformedASTError
├── ValidationError
│ ├── ConfigValidationError
│ ├── SchemaValidationError
│ └── DataValidationError
├── ConfigError
│ ├── ConfigNotFoundError
│ ├── InvalidConfigError
│ └── ConfigMigrationError
├── AuthenticationError
│ ├── InvalidCredentialsError
│ ├── TokenExpiredError
│ └── TokenInvalidError
├── AuthorizationError
│ ├── FeatureNotEntitledError
│ ├── PlanLimitExceededError
│ └── DeviceLimitExceededError
├── NetworkError
│ ├── ConnectionError
│ ├── TimeoutError
│ └── APIError
├── FileSystemError
│ ├── FileNotFoundError
│ ├── PermissionError
│ └── DiskFullError
├── AnalysisError
│ ├── CrossFileTrackingError
│ ├── PropagationError
│ └── CFGBuildError
├── AIError
│ ├── ProviderError
│ ├── ModelNotAvailableError
│ └── SafetyViolationError
└── QuickFixError
├── TransformationError
├── UnsafeOperationError
└── InvalidExpressionError
Base Exception
BranchPyError
Base class for all BranchPy exceptions.
class BranchPyError(Exception):
def __init__(
self,
message: str,
code: str,
details: Optional[Dict[str, Any]] = None
):
self.message = message
self.code = code
self.details = details or {}
super().__init__(message)
Attributes:
message: Human-readable error messagecode: Error code (e.g.,"E_PARSE_001")details: Additional context (file path, line number, etc.)
Example:
try:
analyze_file("script.rpy")
except BranchPyError as e:
print(f"Error {e.code}: {e.message}")
print(f"Details: {e.details}")
Parse Errors
ParseError
Base class for parsing failures.
Error Codes:
E_PARSE_001: Generic parse errorE_PARSE_002: Syntax errorE_PARSE_003: Invalid Python codeE_PARSE_004: Malformed AST
SyntaxError
Raised when script contains invalid syntax.
class SyntaxError(ParseError):
def __init__(
self,
message: str,
file_path: str,
line_number: int,
column: Optional[int] = None
):
details = {
"file_path": file_path,
"line_number": line_number,
"column": column
}
super().__init__(message, "E_PARSE_002", details)
Example:
try:
parser.parse_file("script.rpy")
except SyntaxError as e:
print(f"Syntax error in {e.details['file_path']}")
print(f"Line {e.details['line_number']}: {e.message}")
InvalidPythonCodeError
Raised when Python block contains invalid Python syntax.
Error Code: E_PARSE_003
Details:
file_path: Script file pathline_number: Line number of Python blockpython_code: The invalid Python code
MalformedASTError
Raised when AST structure is invalid or incomplete.
Error Code: E_PARSE_004
Validation Errors
ValidationError
Base class for validation failures.
Error Codes:
E_VALID_001: Generic validation errorE_VALID_002: Config validation errorE_VALID_003: Schema validation errorE_VALID_004: Data validation error
ConfigValidationError
Raised when configuration is invalid.
class ConfigValidationError(ValidationError):
def __init__(
self,
message: str,
config_key: str,
expected_type: str,
actual_value: Any
):
details = {
"config_key": config_key,
"expected_type": expected_type,
"actual_value": str(actual_value)
}
super().__init__(message, "E_VALID_002", details)
Example:
try:
config.set_setting("max_workers", "not_a_number")
except ConfigValidationError as e:
print(f"Invalid config for {e.details['config_key']}")
print(f"Expected {e.details['expected_type']}, got {e.details['actual_value']}")
SchemaValidationError
Raised when data doesn’t match expected schema.
Error Code: E_VALID_003
Details:
schema_path: JSON schema pathvalidation_errors: List of validation failures
DataValidationError
Raised when input data is invalid.
Error Code: E_VALID_004
Details:
field_name: Invalid fieldconstraint: Constraint violatedvalue: Invalid value
Configuration Errors
ConfigError
Base class for configuration issues.
Error Codes:
E_CONFIG_001: Generic config errorE_CONFIG_002: Config not foundE_CONFIG_003: Invalid config formatE_CONFIG_004: Config migration failed
ConfigNotFoundError
Raised when configuration file not found.
Error Code: E_CONFIG_002
Details:
config_path: Expected config file pathsearched_paths: List of searched locations
InvalidConfigError
Raised when config file has invalid format.
Error Code: E_CONFIG_003
ConfigMigrationError
Raised when config migration fails.
Error Code: E_CONFIG_004
Details:
from_version: Source versionto_version: Target versionmigration_errors: List of migration failures
Authentication Errors
AuthenticationError
Base class for authentication failures.
Error Codes:
E_AUTH_001: Generic authentication errorE_AUTH_002: Invalid credentialsE_AUTH_003: Token expiredE_AUTH_004: Token invalid
InvalidCredentialsError
Raised when login credentials are invalid.
class InvalidCredentialsError(AuthenticationError):
def __init__(self, email: str):
details = {"email": email}
super().__init__(
"Invalid email or password",
"E_AUTH_002",
details
)
Example:
try:
login("user@example.com", "wrong_password")
except InvalidCredentialsError as e:
print(f"Login failed for {e.details['email']}")
TokenExpiredError
Raised when access token or refresh token has expired.
Error Code: E_AUTH_003
Details:
token_type:"access"or"refresh"expired_at: Expiration timestamp
Handling:
try:
api_call()
except TokenExpiredError as e:
if e.details['token_type'] == 'access':
# Attempt refresh
refresh_token()
else:
# Re-login required
login()
TokenInvalidError
Raised when token is malformed or tampered.
Error Code: E_AUTH_004
Authorization Errors
AuthorizationError
Base class for authorization failures.
Error Codes:
E_AUTHZ_001: Generic authorization errorE_AUTHZ_002: Feature not entitledE_AUTHZ_003: Plan limit exceededE_AUTHZ_004: Device limit exceeded
FeatureNotEntitledError
Raised when user’s plan doesn’t include requested feature.
class FeatureNotEntitledError(AuthorizationError):
def __init__(
self,
feature_key: str,
current_plan: str,
required_plans: List[str]
):
details = {
"feature_key": feature_key,
"current_plan": current_plan,
"required_plans": required_plans
}
super().__init__(
f"Feature '{feature_key}' requires {', '.join(required_plans)} plan",
"E_AUTHZ_002",
details
)
Example:
try:
require_feature("ai_review")
except FeatureNotEntitledError as e:
feature = e.details['feature_key']
current = e.details['current_plan']
required = ', '.join(e.details['required_plans'])
print(f"Feature {feature} not available on {current} plan")
print(f"Upgrade to: {required}")
PlanLimitExceededError
Raised when plan limits are exceeded.
Error Code: E_AUTHZ_003
Details:
limit_type: Type of limit (e.g.,"api_calls","file_count")current_value: Current usagelimit_value: Maximum allowedreset_at: When limit resets (if applicable)
DeviceLimitExceededError
Raised when device limit reached.
Error Code: E_AUTHZ_004
Details:
device_count: Current device countdevice_limit: Maximum devices allowedregistered_devices: List of registered device IDs
Network Errors
NetworkError
Base class for network-related failures.
Error Codes:
E_NET_001: Generic network errorE_NET_002: Connection failedE_NET_003: Request timeoutE_NET_004: API error
ConnectionError
Raised when unable to connect to service.
Error Code: E_NET_002
Details:
url: Target URLretry_count: Number of retries attempted
TimeoutError
Raised when request times out.
Error Code: E_NET_003
Details:
url: Target URLtimeout_seconds: Configured timeout
APIError
Raised when API returns error response.
class APIError(NetworkError):
def __init__(
self,
message: str,
status_code: int,
response_body: Optional[str] = None
):
details = {
"status_code": status_code,
"response_body": response_body
}
super().__init__(message, "E_NET_004", details)
Example:
try:
api_client.call_endpoint()
except APIError as e:
status = e.details['status_code']
if status == 429:
print("Rate limited, retry after delay")
elif status >= 500:
print("Server error, retry later")
File System Errors
FileSystemError
Base class for filesystem operations.
Error Codes:
E_FS_001: Generic filesystem errorE_FS_002: File not foundE_FS_003: Permission deniedE_FS_004: Disk full
FileNotFoundError
Raised when file doesn’t exist.
Error Code: E_FS_002
Details:
file_path: Missing file pathoperation: Attempted operation (e.g.,"read","parse")
PermissionError
Raised when insufficient permissions.
Error Code: E_FS_003
DiskFullError
Raised when disk space exhausted.
Error Code: E_FS_004
Analysis Errors
AnalysisError
Base class for analysis failures.
Error Codes:
E_ANALYSIS_001: Generic analysis errorE_ANALYSIS_002: Cross-file tracking errorE_ANALYSIS_003: Propagation errorE_ANALYSIS_004: CFG build error
CrossFileTrackingError
Raised when cross-file tracking fails.
Error Code: E_ANALYSIS_002
Details:
file_path: File being analyzedreference_type: Type of reference (e.g.,"label","call")reference_name: Unresolved reference
PropagationError
Raised when expression propagation fails.
Error Code: E_ANALYSIS_003
CFGBuildError
Raised when CFG construction fails.
Error Code: E_ANALYSIS_004
AI Errors
AIError
Base class for AI-related failures.
Error Codes:
E_AI_001: Generic AI errorE_AI_002: Provider errorE_AI_003: Model not availableE_AI_004: Safety violation
ProviderError
Raised when AI provider returns error.
class ProviderError(AIError):
def __init__(
self,
provider_id: str,
error_message: str,
provider_code: Optional[str] = None
):
details = {
"provider_id": provider_id,
"provider_code": provider_code,
"provider_message": error_message
}
super().__init__(
f"Provider {provider_id} error: {error_message}",
"E_AI_002",
details
)
Example:
try:
reviewer.review_code(source_code)
except ProviderError as e:
provider = e.details['provider_id']
msg = e.details['provider_message']
print(f"AI provider {provider} failed: {msg}")
ModelNotAvailableError
Raised when requested model not available.
Error Code: E_AI_003
Details:
model_name: Requested modelprovider_id: Provideravailable_models: List of available models
SafetyViolationError
Raised when content violates safety policies.
Error Code: E_AI_004
Details:
violation_type: Type of violationcontent_snippet: Snippet that triggered violation (redacted)
QuickFix Errors
QuickFixError
Base class for QuickFix operation failures.
Error Codes:
E_QUICKFIX_001: Generic QuickFix errorE_QUICKFIX_002: Transformation errorE_QUICKFIX_003: Unsafe operationE_QUICKFIX_004: Invalid expression
TransformationError
Raised when code transformation fails.
Error Code: E_QUICKFIX_002
Details:
operation: Operation type (e.g.,"inline","extract")file_path: Target fileline_number: Target linereason: Why transformation failed
UnsafeOperationError
Raised when operation would change code semantics.
Error Code: E_QUICKFIX_003
Details:
operation: Operation typesafety_issue: Description of safety concern
InvalidExpressionError
Raised when expression is malformed or unsupported.
Error Code: E_QUICKFIX_004
Error Handling Patterns
Try-Except Blocks
from branchpy.errors import (
BranchPyError,
ParseError,
AuthenticationError,
FeatureNotEntitledError
)
try:
# Operation
result = analyze_file("script.rpy")
except ParseError as e:
# Handle parse errors
logger.error(f"Parse failed: {e.message}", extra=e.details)
except FeatureNotEntitledError as e:
# Handle entitlement issues
show_upgrade_prompt(e.details['required_plans'])
except AuthenticationError as e:
# Handle auth failures
prompt_login()
except BranchPyError as e:
# Catch-all for BranchPy errors
logger.error(f"Error {e.code}: {e.message}")
except Exception as e:
# Unexpected errors
logger.critical(f"Unexpected error: {e}", exc_info=True)
Error Context Managers
from contextlib import contextmanager
from branchpy.errors import BranchPyError
@contextmanager
def error_context(operation: str):
"""Add context to errors"""
try:
yield
except BranchPyError as e:
e.details["operation"] = operation
raise
except Exception as e:
# Wrap unexpected errors
raise BranchPyError(
message=f"Unexpected error during {operation}: {str(e)}",
code="E_UNEXPECTED",
details={"operation": operation, "original_error": str(e)}
) from e
# Usage
with error_context("file analysis"):
analyze_file("script.rpy")
Retry Logic
from branchpy.errors import NetworkError, TimeoutError
import time
def retry_on_network_error(func, max_retries=3, delay=1.0):
"""Retry function on network errors"""
for attempt in range(max_retries):
try:
return func()
except (NetworkError, TimeoutError) as e:
if attempt == max_retries - 1:
raise
logger.warning(f"Attempt {attempt + 1} failed: {e.message}")
time.sleep(delay * (attempt + 1))
# Usage
result = retry_on_network_error(lambda: api_client.fetch_data())
Error Recovery
from branchpy.errors import TokenExpiredError, InvalidCredentialsError
def api_call_with_recovery():
"""API call with automatic token refresh"""
try:
return api_client.call()
except TokenExpiredError as e:
if e.details['token_type'] == 'access':
# Refresh token and retry
refresh_token()
return api_client.call()
else:
# Refresh token expired, prompt login
raise InvalidCredentialsError("Session expired, please login")
Logging Errors
from branchpy.errors import BranchPyError
from branchpy.logging import get_logger
logger = get_logger(__name__)
try:
operation()
except BranchPyError as e:
# Structured logging with error details
logger.error(
e.message,
extra={
"error_code": e.code,
"error_details": e.details,
"operation": "analyze",
"traceback": True
}
)
Error Response Format
When errors occur in API calls or CLI commands, they follow a consistent format:
CLI Error Output
Error [E_PARSE_002]: Syntax error in script.rpy
Line 42: Unexpected indent
Details:
file_path: /path/to/script.rpy
line_number: 42
column: 4
JSON Error Response
{
"success": false,
"error": {
"code": "E_PARSE_002",
"message": "Syntax error in script.rpy",
"details": {
"file_path": "/path/to/script.rpy",
"line_number": 42,
"column": 4
}
}
}
VS Code Extension Errors
Displayed in Problems panel:
script.rpy [42:4] Error (E_PARSE_002): Syntax error: Unexpected indent
Exit Codes
CLI commands use standard exit codes:
| Code | Meaning | Error Types |
|---|---|---|
| 0 | Success | None |
| 1 | General error | Unspecified failures |
| 2 | Parse error | ParseError |
| 3 | Validation error | ValidationError |
| 4 | Config error | ConfigError |
| 5 | Authentication error | AuthenticationError |
| 6 | Authorization error | AuthorizationError |
| 7 | Network error | NetworkError |
| 8 | File system error | FileSystemError |
| 9 | Analysis error | AnalysisError |
| 10 | AI error | AIError |
| 11 | QuickFix error | QuickFixError |
Exit Code Usage
import sys
from branchpy.errors import BranchPyError, ParseError, AuthenticationError
try:
main()
sys.exit(0) # Success
except ParseError:
sys.exit(2)
except AuthenticationError:
sys.exit(5)
except BranchPyError:
sys.exit(1)
except Exception:
sys.exit(1)
Error Constants
Common error constants defined in branchpy.contract:
# Exit codes
EXIT_SUCCESS = 0
EXIT_ERROR = 1
EXIT_PARSE_ERROR = 2
EXIT_VALIDATION_ERROR = 3
EXIT_CONFIG_ERROR = 4
EXIT_AUTH_ERROR = 5
EXIT_AUTHZ_ERROR = 6
EXIT_NET_ERROR = 7
EXIT_FS_ERROR = 8
EXIT_ANALYSIS_ERROR = 9
EXIT_AI_ERROR = 10
EXIT_QUICKFIX_ERROR = 11
# Error code prefixes
ERROR_PREFIX_PARSE = "E_PARSE"
ERROR_PREFIX_VALID = "E_VALID"
ERROR_PREFIX_CONFIG = "E_CONFIG"
ERROR_PREFIX_AUTH = "E_AUTH"
ERROR_PREFIX_AUTHZ = "E_AUTHZ"
ERROR_PREFIX_NET = "E_NET"
ERROR_PREFIX_FS = "E_FS"
ERROR_PREFIX_ANALYSIS = "E_ANALYSIS"
ERROR_PREFIX_AI = "E_AI"
ERROR_PREFIX_QUICKFIX = "E_QUICKFIX"
Best Practices
1. Use Specific Exceptions
# Good
raise InvalidCredentialsError(email="user@example.com")
# Avoid
raise BranchPyError("Invalid credentials", "E_AUTH_002")
2. Include Context in Details
# Good
raise ParseError(
message="Syntax error",
code="E_PARSE_002",
details={
"file_path": file_path,
"line_number": line_num,
"column": col,
"surrounding_lines": context
}
)
# Avoid
raise ParseError("Syntax error", "E_PARSE_002")
3. Log Before Raising
logger.error(
"Parse failed",
extra={
"file": file_path,
"line": line_num,
"error": error_msg
}
)
raise ParseError(error_msg, file_path, line_num)
4. Handle Errors Gracefully
# Provide helpful error messages
try:
require_feature("ai_review")
except FeatureNotEntitledError as e:
print(f"⚠️ AI Review requires Pro plan")
print(f"Your current plan: {e.details['current_plan']}")
print(f"Upgrade at: https://branchpy.com/upgrade")
5. Test Error Handling
import pytest
from branchpy.errors import ParseError
def test_parse_error_handling():
with pytest.raises(ParseError) as exc_info:
parser.parse_file("invalid.rpy")
assert exc_info.value.code == "E_PARSE_002"
assert "file_path" in exc_info.value.details
Cross-References
Related Documentation
- API Contracts - Request/response formats and error handling
- README - API overview
- Technical Errors - Error classification system
- Backend Architecture - Backend error handling
- Logging - Error logging configuration
Source References:
- Original Error Documentation:
docs/v1.1.0/API/errors.md - Exception Hierarchy:
BranchPyApp/branchpy/errors.py - Error Constants:
BranchPyApp/branchpy/contract.py - Version: 1.1.1
- Last Updated: January 23, 2026