CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-obsws-python

A Python SDK for OBS Studio WebSocket v5.0

Pending
Overview
Eval results
Files

error-handling.mddocs/

Error Handling

obsws-python provides comprehensive error handling with specific exception types for different failure scenarios. Understanding these errors is crucial for building robust OBS automation applications.

Exception Hierarchy

Base Exception

class OBSSDKError(Exception):
    """Base class for all OBSSDK errors."""

Specific Exception Types

class OBSSDKTimeoutError(OBSSDKError):
    """
    Exception raised when a connection times out.
    
    Raised when:
    - Connection to OBS WebSocket times out
    - Request sends but response times out
    - Event receiving times out
    """

class OBSSDKRequestError(OBSSDKError):
    """
    Exception raised when a request returns an error code.
    
    Attributes:
    - req_name (str): Name of the failed request
    - code (int): OBS WebSocket error code
    """
    
    def __init__(self, req_name, code, comment):
        self.req_name = req_name
        self.code = code
        # message automatically formatted as: "Request {req_name} returned code {code}. With message: {comment}"

Common Error Scenarios

Connection Errors

import obsws_python as obs
from obsws_python.error import OBSSDKError, OBSSDKTimeoutError

try:
    # Wrong host/port
    client = obs.ReqClient(host='nonexistent', port=9999, timeout=5)
except (ConnectionRefusedError, TimeoutError) as e:
    print(f"Connection failed: {e}")
except OBSSDKError as e:
    print(f"OBS SDK error: {e}")

try:
    # Wrong password
    client = obs.ReqClient(password='wrongpassword')
except OBSSDKError as e:
    print(f"Authentication failed: {e}")

try:
    # Connection timeout
    client = obs.ReqClient(host='slow-server.com', timeout=1)
except OBSSDKTimeoutError as e:
    print(f"Connection timed out: {e}")

Request Errors

import obsws_python as obs
from obsws_python.error import OBSSDKRequestError

client = obs.ReqClient()

try:
    # Non-existent scene
    client.set_current_program_scene("NonExistentScene")
except OBSSDKRequestError as e:
    print(f"Request failed: {e.req_name}")
    print(f"Error code: {e.code}")
    print(f"Full error: {e}")

try:
    # Invalid input name
    client.toggle_input_mute("NonExistentInput")
except OBSSDKRequestError as e:
    if e.code == 600:  # ResourceNotFound
        print(f"Input not found: {e.req_name}")
    else:
        print(f"Unexpected error {e.code}: {e}")

Event Client Errors

import obsws_python as obs
from obsws_python.error import OBSSDKTimeoutError

try:
    client = obs.EventClient(timeout=10)
    
    # Event client runs in background thread
    # Errors typically occur during connection or event processing
    
except OBSSDKTimeoutError as e:
    print(f"Event client timed out: {e}")
except Exception as e:
    print(f"Event client error: {e}")

Error Codes

OBS WebSocket returns specific error codes for different failure scenarios:

Common Error Codes

CodeNameDescription
100SuccessRequest completed successfully
200MissingRequestTypeRequest type not specified
201UnknownRequestTypeUnknown request type
202GenericErrorGeneric error occurred
203UnsupportedRequestBatchExecutionTypeUnsupported batch execution type
300MissingRequestFieldRequired request field missing
301MissingRequestDataRequest data missing
400InvalidRequestFieldInvalid request field value
401InvalidRequestFieldTypeInvalid request field type
402RequestFieldOutOfRangeRequest field value out of range
403RequestFieldEmptyRequest field is empty
500OutputRunningOutput is running
501OutputNotRunningOutput is not running
502OutputPausedOutput is paused
503OutputNotPausedOutput is not paused
504OutputDisabledOutput is disabled
505StudioModeActiveStudio mode is active
506StudioModeNotActiveStudio mode is not active
600ResourceNotFoundResource not found
601ResourceAlreadyExistsResource already exists
602InvalidResourceTypeInvalid resource type
603NotEnoughResourcesNot enough resources
604InvalidResourceStateInvalid resource state
605InvalidInputKindInvalid input kind
606ResourceNotConfigurableResource not configurable
607InvalidFilterKindInvalid filter kind

Error Handling Patterns

Basic Error Handling

import obsws_python as obs
from obsws_python.error import OBSSDKError, OBSSDKRequestError, OBSSDKTimeoutError

def safe_obs_operation(client, operation_name, operation_func):
    """
    Safely execute an OBS operation with error handling.
    
    Parameters:
    - client: OBS client instance  
    - operation_name: Description of operation for logging
    - operation_func: Function to execute
    
    Returns:
    - (success: bool, result: any, error: str)
    """
    try:
        result = operation_func()
        print(f"✅ {operation_name} succeeded")
        return True, result, None
        
    except OBSSDKRequestError as e:
        error_msg = f"{operation_name} failed: {e.req_name} returned code {e.code}"
        print(f"❌ {error_msg}")
        return False, None, error_msg
        
    except OBSSDKTimeoutError as e:
        error_msg = f"{operation_name} timed out: {e}"
        print(f"⏱️ {error_msg}")
        return False, None, error_msg
        
    except OBSSDKError as e:
        error_msg = f"{operation_name} failed: {e}"
        print(f"💥 {error_msg}")
        return False, None, error_msg

# Usage
client = obs.ReqClient()

success, result, error = safe_obs_operation(
    client, 
    "Scene switch",
    lambda: client.set_current_program_scene("Scene 2")
)

if success:
    print("Scene switched successfully")
else:
    print(f"Scene switch failed: {error}")

Retry Logic

import obsws_python as obs
from obsws_python.error import OBSSDKTimeoutError, OBSSDKRequestError
import time

def retry_operation(operation_func, max_retries=3, delay=1.0):
    """
    Retry an OBS operation with exponential backoff.
    
    Parameters:
    - operation_func: Function to retry
    - max_retries: Maximum number of retry attempts
    - delay: Initial delay between retries (doubles each attempt)
    
    Returns:
    - Result of successful operation or raises last exception
    """
    last_exception = None
    
    for attempt in range(max_retries + 1):
        try:
            return operation_func()
            
        except OBSSDKTimeoutError as e:
            last_exception = e
            if attempt < max_retries:
                wait_time = delay * (2 ** attempt)
                print(f"Timeout on attempt {attempt + 1}, retrying in {wait_time}s...")
                time.sleep(wait_time)
            else:
                print(f"Operation failed after {max_retries + 1} attempts")
                
        except OBSSDKRequestError as e:
            # Don't retry request errors (they won't succeed)
            print(f"Request error (not retrying): {e}")
            raise
            
        except OBSSDKError as e:
            last_exception = e
            if attempt < max_retries:
                print(f"Error on attempt {attempt + 1}, retrying...")
                time.sleep(delay)
            else:
                print(f"Operation failed after {max_retries + 1} attempts")
    
    raise last_exception

# Usage
client = obs.ReqClient()

try:
    result = retry_operation(
        lambda: client.get_version(),
        max_retries=3,
        delay=1.0
    )
    print(f"Got version: {result.obs_version}")
    
except Exception as e:
    print(f"Failed to get version after retries: {e}")

Context Manager with Error Handling

import obsws_python as obs
from obsws_python.error import OBSSDKError
from contextlib import contextmanager

@contextmanager
def obs_client_safe(**kwargs):
    """
    Context manager for safe OBS client creation and cleanup.
    """
    client = None
    try:
        client = obs.ReqClient(**kwargs)
        print("✅ Connected to OBS")
        yield client
        
    except OBSSDKError as e:
        print(f"❌ Failed to connect to OBS: {e}")
        raise
        
    finally:
        if client:
            try:
                client.disconnect()
                print("🔌 Disconnected from OBS")
            except Exception as e:
                print(f"⚠️ Error during disconnect: {e}")

# Usage
try:
    with obs_client_safe(timeout=10) as client:
        # Do operations with error handling
        try:
            version = client.get_version()
            print(f"OBS Version: {version.obs_version}")
            
            client.set_current_program_scene("Scene 1")
            print("Scene changed successfully")
            
        except OBSSDKRequestError as e:
            print(f"Operation failed: {e}")
            # Continue with other operations...
            
except OBSSDKError:
    print("Could not establish OBS connection")

Event Client Error Handling

import obsws_python as obs
from obsws_python.error import OBSSDKError, OBSSDKTimeoutError
import threading
import time

class RobustEventClient:
    def __init__(self, **kwargs):
        self.client = None
        self.connection_params = kwargs
        self.running = False
        self.reconnect_attempts = 0
        self.max_reconnects = 5
        
    def connect(self):
        """Connect with error handling and retry logic."""
        while self.reconnect_attempts < self.max_reconnects:
            try:
                if self.client:
                    self.client.disconnect()
                    
                self.client = obs.EventClient(**self.connection_params)
                self.reconnect_attempts = 0
                print("✅ Event client connected")
                return True
                
            except OBSSDKTimeoutError as e:
                self.reconnect_attempts += 1
                wait_time = min(30, 2 ** self.reconnect_attempts)
                print(f"⏱️ Connection timeout (attempt {self.reconnect_attempts}), retrying in {wait_time}s...")
                time.sleep(wait_time)
                
            except OBSSDKError as e:
                print(f"❌ Connection failed: {e}")
                return False
                
        print(f"💥 Failed to connect after {self.max_reconnects} attempts")
        return False
    
    def register_callbacks(self, callbacks):
        """Register event callbacks with error handling."""
        if not self.client:
            print("❌ No client connected")
            return False
            
        try:
            self.client.callback.register(callbacks)
            return True
        except Exception as e:
            print(f"❌ Failed to register callbacks: {e}")
            return False
    
    def start_monitoring(self):
        """Start monitoring with automatic reconnection."""
        self.running = True
        
        def monitor_loop():
            while self.running:
                if not self.client or not self.connect():
                    time.sleep(5)
                    continue
                    
                try:
                    # Keep connection alive
                    while self.running:
                        time.sleep(1)
                        
                except Exception as e:
                    print(f"⚠️ Event monitoring error: {e}")
                    self.reconnect_attempts += 1
                    time.sleep(2)
        
        self.monitor_thread = threading.Thread(target=monitor_loop, daemon=True)
        self.monitor_thread.start()
    
    def stop_monitoring(self):
        """Stop monitoring and disconnect."""
        self.running = False
        if self.client:
            try:
                self.client.disconnect()
            except Exception as e:
                print(f"⚠️ Error during disconnect: {e}")

# Usage
def on_scene_changed(data):
    print(f"Scene changed: {data.scene_name}")

def on_connection_error(error):
    print(f"Connection error: {error}")

# Create robust event client
event_client = RobustEventClient(
    host='localhost',
    port=4455,
    password='mypassword',
    timeout=10
)

if event_client.connect():
    event_client.register_callbacks([on_scene_changed])
    event_client.start_monitoring()
    
    try:
        input("Event monitoring active. Press Enter to stop...\n")
    finally:
        event_client.stop_monitoring()
else:
    print("Failed to establish initial connection")

Debugging Tips

Enable Logging

import logging
import obsws_python as obs

# Enable debug logging to see raw WebSocket messages
logging.basicConfig(level=logging.DEBUG)

client = obs.ReqClient()
# Now you'll see detailed logs of all WebSocket communication

Error Analysis Helper

import obsws_python as obs
from obsws_python.error import OBSSDKRequestError

def analyze_request_error(error):
    """Analyze OBS request error and provide helpful information."""
    if not isinstance(error, OBSSDKRequestError):
        return f"Not an OBSSDKRequestError: {type(error).__name__}"
    
    analysis = f"Request: {error.req_name}\nError Code: {error.code}\n"
    
    # Common error code explanations
    explanations = {
        600: "Resource not found - check names are correct and resources exist",
        601: "Resource already exists - trying to create something that already exists",
        500: "Output already running - trying to start already active output",
        501: "Output not running - trying to stop inactive output",
        505: "Studio mode is active - operation requires studio mode to be disabled",
        506: "Studio mode not active - operation requires studio mode to be enabled",
        402: "Value out of range - check parameter bounds",
        403: "Field is empty - required parameter is missing or empty"
    }
    
    if error.code in explanations:
        analysis += f"Likely cause: {explanations[error.code]}"
    else:
        analysis += "Check OBS WebSocket documentation for error code details"
    
    return analysis

# Usage
client = obs.ReqClient()

try:
    client.set_current_program_scene("NonExistentScene")
except OBSSDKRequestError as e:
    print(analyze_request_error(e))

Install with Tessl CLI

npx tessl i tessl/pypi-obsws-python

docs

error-handling.md

event-client.md

event-subscriptions.md

index.md

request-client.md

tile.json