Docs-Builder


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 message
  • code: 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 error
  • E_PARSE_002: Syntax error
  • E_PARSE_003: Invalid Python code
  • E_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 path
  • line_number: Line number of Python block
  • python_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 error
  • E_VALID_002: Config validation error
  • E_VALID_003: Schema validation error
  • E_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 path
  • validation_errors: List of validation failures

DataValidationError

Raised when input data is invalid.

Error Code: E_VALID_004

Details:

  • field_name: Invalid field
  • constraint: Constraint violated
  • value: Invalid value

Configuration Errors

ConfigError

Base class for configuration issues.

Error Codes:

  • E_CONFIG_001: Generic config error
  • E_CONFIG_002: Config not found
  • E_CONFIG_003: Invalid config format
  • E_CONFIG_004: Config migration failed

ConfigNotFoundError

Raised when configuration file not found.

Error Code: E_CONFIG_002

Details:

  • config_path: Expected config file path
  • searched_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 version
  • to_version: Target version
  • migration_errors: List of migration failures

Authentication Errors

AuthenticationError

Base class for authentication failures.

Error Codes:

  • E_AUTH_001: Generic authentication error
  • E_AUTH_002: Invalid credentials
  • E_AUTH_003: Token expired
  • E_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 error
  • E_AUTHZ_002: Feature not entitled
  • E_AUTHZ_003: Plan limit exceeded
  • E_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 usage
  • limit_value: Maximum allowed
  • reset_at: When limit resets (if applicable)

DeviceLimitExceededError

Raised when device limit reached.

Error Code: E_AUTHZ_004

Details:

  • device_count: Current device count
  • device_limit: Maximum devices allowed
  • registered_devices: List of registered device IDs

Network Errors

NetworkError

Base class for network-related failures.

Error Codes:

  • E_NET_001: Generic network error
  • E_NET_002: Connection failed
  • E_NET_003: Request timeout
  • E_NET_004: API error

ConnectionError

Raised when unable to connect to service.

Error Code: E_NET_002

Details:

  • url: Target URL
  • retry_count: Number of retries attempted

TimeoutError

Raised when request times out.

Error Code: E_NET_003

Details:

  • url: Target URL
  • timeout_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 error
  • E_FS_002: File not found
  • E_FS_003: Permission denied
  • E_FS_004: Disk full

FileNotFoundError

Raised when file doesn’t exist.

Error Code: E_FS_002

Details:

  • file_path: Missing file path
  • operation: 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 error
  • E_ANALYSIS_002: Cross-file tracking error
  • E_ANALYSIS_003: Propagation error
  • E_ANALYSIS_004: CFG build error

CrossFileTrackingError

Raised when cross-file tracking fails.

Error Code: E_ANALYSIS_002

Details:

  • file_path: File being analyzed
  • reference_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 error
  • E_AI_002: Provider error
  • E_AI_003: Model not available
  • E_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 model
  • provider_id: Provider
  • available_models: List of available models

SafetyViolationError

Raised when content violates safety policies.

Error Code: E_AI_004

Details:

  • violation_type: Type of violation
  • content_snippet: Snippet that triggered violation (redacted)

QuickFix Errors

QuickFixError

Base class for QuickFix operation failures.

Error Codes:

  • E_QUICKFIX_001: Generic QuickFix error
  • E_QUICKFIX_002: Transformation error
  • E_QUICKFIX_003: Unsafe operation
  • E_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 file
  • line_number: Target line
  • reason: Why transformation failed

UnsafeOperationError

Raised when operation would change code semantics.

Error Code: E_QUICKFIX_003

Details:

  • operation: Operation type
  • safety_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


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