A Python SDK for OBS Studio WebSocket v5.0
—
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.
class OBSSDKError(Exception):
"""Base class for all OBSSDK errors."""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}"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}")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}")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}")OBS WebSocket returns specific error codes for different failure scenarios:
| Code | Name | Description |
|---|---|---|
| 100 | Success | Request completed successfully |
| 200 | MissingRequestType | Request type not specified |
| 201 | UnknownRequestType | Unknown request type |
| 202 | GenericError | Generic error occurred |
| 203 | UnsupportedRequestBatchExecutionType | Unsupported batch execution type |
| 300 | MissingRequestField | Required request field missing |
| 301 | MissingRequestData | Request data missing |
| 400 | InvalidRequestField | Invalid request field value |
| 401 | InvalidRequestFieldType | Invalid request field type |
| 402 | RequestFieldOutOfRange | Request field value out of range |
| 403 | RequestFieldEmpty | Request field is empty |
| 500 | OutputRunning | Output is running |
| 501 | OutputNotRunning | Output is not running |
| 502 | OutputPaused | Output is paused |
| 503 | OutputNotPaused | Output is not paused |
| 504 | OutputDisabled | Output is disabled |
| 505 | StudioModeActive | Studio mode is active |
| 506 | StudioModeNotActive | Studio mode is not active |
| 600 | ResourceNotFound | Resource not found |
| 601 | ResourceAlreadyExists | Resource already exists |
| 602 | InvalidResourceType | Invalid resource type |
| 603 | NotEnoughResources | Not enough resources |
| 604 | InvalidResourceState | Invalid resource state |
| 605 | InvalidInputKind | Invalid input kind |
| 606 | ResourceNotConfigurable | Resource not configurable |
| 607 | InvalidFilterKind | Invalid filter kind |
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}")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}")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")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")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 communicationimport 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