7. Exception Hierarchy & Handling¶
7.1 Exception Base & Device Errors¶
Base exception and specific device error subclasses. The Pythonic API raises these automatically; Result-based API returns error codes instead.
DuvcError - base exception with error context¶
All exceptions inherit from DuvcError. Provides unified error handling, error codes, and contextual information for debugging.
Attributes:
error_code(DuvcErrorCode or None): Integer error code mapping to specific conditions.context(str or None): Additional context information describing where/why error occurred.
Methods:
__str__(): Returns formatted string with error code name, message, and context.
DuvcErrorCode IntEnum (0-8):
Code |
Name |
Value |
Meaning |
|---|---|---|---|
Success |
|
0 |
Operation succeeded |
DeviceNotFound |
|
1 |
Device not found or disconnected |
DeviceBusy |
|
2 |
Device busy or in use |
PropertyNotSupported |
|
3 |
Property not supported by device |
InvalidValue |
|
4 |
Property value out of range |
PermissionDenied |
|
5 |
Insufficient permissions |
SystemError |
|
6 |
System or platform error |
InvalidArgument |
|
7 |
Invalid function argument |
NotImplemented |
|
8 |
Feature not implemented |
Usage:
try:
camera = duvc.CameraController(0)
except duvc.DuvcError as e:
print(f"Error: {e}")
print(f"Code: {e.error_code}")
print(f"Context: {e.context}")
DeviceNotFoundError - device not accessible¶
Raised when device not found, disconnected, or not recognized by system. Device may have been physically disconnected or is not enumerable.
Typical causes: Device unplugged, driver issues, hardware failure, USB hub power-off.
Recovery: Reconnect device, check Device Manager, reinstall drivers.
try:
camera = duvc.CameraController(0)
except duvc.DeviceNotFoundError as e:
print(f"Device not found: {e}")
devices = duvc.list_devices()
print(f"Available devices: {len(devices)}")
for dev in devices:
print(f" - {dev.name}")
DeviceBusyError - device locked by another process¶
Raised when device is already open in another application or thread. Exclusive access prevents concurrent opens (DirectShow limitation).
Typical causes: Camera open in another app, another thread holding camera, previous open not properly closed.
Recovery: Close other apps using camera, ensure all camera objects are released.
try:
camera = duvc.CameraController(0)
except duvc.DeviceBusyError as e:
print(f"Camera in use: {e}")
print("Close other applications and retry")
InvalidValueError - property value outside valid range¶
Raised when attempting to set property to value outside [min, max] bounds or with invalid type.
Typical causes: Value out of range, step size mismatch, type error.
Recovery: Query range first, clamp value to bounds, use correct type.
try:
camera = duvc.CameraController(0)
camera.set_brightness(500) # Typical range 0-255
except duvc.InvalidValueError as e:
print(f"Invalid value: {e}")
caps = camera.get_capabilities()
bright_range = caps.video_properties['Brightness']['range']
print(f"Valid range: {bright_range['min']}-{bright_range['max']}")
PermissionDeniedError - insufficient access permissions¶
Raised when lacking required permissions to access device. Can occur if privacy settings block camera or insufficient OS privileges.
Typical causes: Privacy mode enabled, app not granted camera permission, insufficient privileges.
Recovery: Grant permissions, disable privacy mode, run elevated.
try:
camera = duvc.CameraController(0)
except duvc.PermissionDeniedError as e:
print(f"Permission denied: {e}")
print("Check privacy settings or run with administrator privileges")
7.2 Property & Value Errors¶
Specialized exception classes for property-specific failures. These provide detailed diagnostic attributes and recovery guidance for common usage errors.
PropertyValueOutOfRangeError - comprehensive range validation¶
Detailed InvalidValueError subclass for out-of-range property values. Captures and reports complete range information with suggested corrections.
Attributes:
property_name(str): Property name (e.g., “Brightness”).value(int): The attempted value that was rejected.min_val(int): Minimum valid value for this property on this device.max_val(int): Maximum valid value for this property on this device.current_val(int or None): Current property value before failed attempt. Helps understand what changed.step(int or None): Step size between valid values. Important for alignment.
Exception message format: Includes clamped suggestion automatically.
"Value 500 is out of range for 'Brightness'. Valid range: 0 to 255
(step: 1). Current value: 128. Try: 255"
Usage:
try:
camera.set_brightness(500) # Range typically 0-255
except duvc.PropertyValueOutOfRangeError as e:
print(f"Error: {e}")
print(f" Property: {e.property_name}")
print(f" Attempted: {e.value}")
print(f" Valid range: {e.min_val}-{e.max_val}")
print(f" Step size: {e.step}")
if e.current_val is not None:
print(f" Current: {e.current_val}")
# Automatic recovery: clamp to valid range
corrected = max(e.min_val, min(e.max_val, e.value))
camera.set_brightness(corrected)
print(f" Retried with: {corrected}")
Recovery algorithm: Exception message automatically suggests clamping target (Try: X). Apply this value directly without re-querying range.
PropertyModeNotSupportedError - mode compatibility checking¶
Distinguishes mode failures from property unavailability. Raised when property exists but requested mode (auto/manual/continuous) isn’t supported.
Attributes:
property_name(str): Property name (e.g., “Pan”).mode(str): Mode requested (“auto”, “manual”, or “continuous”).supported_modes(list[str]): List of actually supported modes on this device. Empty if information unavailable.
Usage:
try:
camera.set_property_auto(duvc.CamProp.Pan) # Pan typically manual-only
except duvc.PropertyModeNotSupportedError as e:
print(f"Mode error: {e}")
print(f" Property: {e.property_name}")
print(f" Requested mode: {e.mode}")
if e.supported_modes:
print(f" Supported modes: {e.supported_modes}")
# Fallback to supported mode
fallback = e.supported_modes[^0] if e.supported_modes else 'manual'
if fallback == 'manual':
camera.set_property_manual(duvc.CamProp.Pan, 0)
else:
# Mode information not available, try manual fallback
camera.set_property_manual(duvc.CamProp.Pan, 0)
Difference from PropertyNotSupportedError: PropertyNotSupportedError = property doesn’t exist. PropertyModeNotSupportedError = property exists but mode unavailable.
InvalidValueError - generic value validation failure¶
Base class for all value validation errors not covered by specific subclasses. Type errors, null values, invalid enum constants.
Typical causes: Wrong type passed (string instead of int), null/None value, invalid enum constant.
Recovery:
try:
camera.set(duvc.VidProp.Brightness, "bright") # Type error
except duvc.InvalidValueError as e:
print(f"Invalid value: {e}")
# Verify type: must be int or PropSetting
camera.set(duvc.VidProp.Brightness, 128) # Correct: int
InvalidArgumentError - function argument validation¶
Raised when function receives structurally invalid argument. Catches programming errors before reaching hardware.
Typical causes: Null device pointer, invalid enum passed, malformed PropSetting structure.
Recovery:
try:
camera.set(None, duvc.PropSetting(100)) # Invalid property enum
except duvc.InvalidArgumentError as e:
print(f"Bad argument: {e}")
print(f"Error code: {e.error_code}")
# Use valid enum constant
camera.set(duvc.VidProp.Brightness, duvc.PropSetting(100))
BulkOperationError - batch operation diagnostics¶
Raised when bulk operations (e.g., setting multiple properties) partially succeed. Contains per-property failure details.
Attributes:
operation(str): Operation name (e.g., “set_properties”).failed_properties(dict): Mapping of property names to error messages.successful_count(int): Number of properties successfully modified.total_count(int): Total properties attempted.
Methods:
get_recovery_suggestions() -> list[str]: Analyzes failures and suggests recovery actions.
Usage:
props_to_set = {
'Brightness': 150,
'Contrast': 80,
'Pan': 45, # May fail if unsupported
}
try:
camera.bulk_set_properties(props_to_set)
except duvc.BulkOperationError as e:
print(f"Bulk operation failed: {e}")
print(f"Succeeded: {e.successful_count}/{e.total_count}")
print(f"Failed properties: {e.failed_properties}")
# Get smart recovery suggestions
for suggestion in e.get_recovery_suggestions():
print(f" → {suggestion}")
# Retry failed properties individually
for prop, error_msg in e.failed_properties.items():
try:
camera.set_single_property(prop, props_to_set[prop])
except duvc.PropertyNotSupportedError:
print(f"Skipping unsupported property: {prop}")
Recovery suggestions algorithm:
“not supported” error → Suggests
get_capabilities()query“out of range” error → Suggests
get_property_range()query“busy” error → Suggests closing other apps
No specific pattern → Suggests individual retry
ConnectionHealthError - connection diagnostics¶
Raised when connection health checks fail. Provides diagnostics and recovery patterns.
Attributes:
device_name(str): Camera device name.health_issues(list[str]): List of detected issues (timeout, property locked, etc.).last_working_operation(str or None): Last successful operation, helps narrow troubleshooting scope.
Methods:
get_recovery_suggestions() -> list[str]: Context-aware recovery steps.
Usage:
try:
# Operations fail due to connection issues
camera.health_check()
except duvc.ConnectionHealthError as e:
print(f"Connection health check failed: {e}")
print(f"Device: {e.device_name}")
print(f"Issues: {', '.join(e.health_issues)}")
if e.last_working_operation:
print(f"Last working operation: {e.last_working_operation}")
# Get recovery suggestions based on failure pattern
for suggestion in e.get_recovery_suggestions():
print(f" → {suggestion}")
# Recovery: try reconnection
try:
camera.reconnect()
except Exception:
# Fallback: restart from scratch
camera = duvc.CameraController(device_index)
Recovery suggestions algorithm:
“timeout” issue → Suggests reconnect
“property locked” issue → Suggests reset_to_defaults()
“no response” issue → Suggests check for concurrent access
Fallback → Suggests cam.reconnect() or application restart
Exception recovery suggestion algorithms¶
The library intelligently analyzes failure patterns and suggests targeted fixes:
PropertyValueOutOfRangeError recovery:
Extract suggested value from exception message
Clamp user input to [min, max]
Respect step size when adjusting
Retry with clamped value
PropertyModeNotSupportedError recovery:
Check supported_modes list
Fall back to first supported mode
If no list, try “manual” as universal fallback
If still failing, property may be truly unsupported
BulkOperationError recovery:
Analyze failure messages for patterns
Group by failure type (unsupported vs. out-of-range vs. busy)
Suggest property-specific diagnostics
Recommend individual retry if batch failed
ConnectionHealthError recovery:
Classify issue type (timeout vs. lock vs. communication)
Match to appropriate action (reconnect vs. reset vs. check access)
Provide last-working context to narrow scope
Escalate to full restart if basic recovery fails
7.3 System & Advanced Exceptions¶
Platform-level exceptions for system integration, connection management, and batch operations. These handle non-property-specific failures and advanced recovery patterns.
PermissionDeniedError - access control failures¶
Raised when lacking OS-level permissions to access device. Privacy settings, restricted mode, or insufficient user privileges.
Typical causes: Privacy mode enabled, camera permission denied by OS, running without admin rights, restricted user account.
Recovery: Grant app camera permission in privacy settings, run elevated, check user group membership.
try:
camera = duvc.CameraController(0)
except duvc.PermissionDeniedError as e:
print(f"Permission denied: {e}")
print("Grant camera access in Settings > Privacy > Camera")
SystemError - platform/driver failures¶
Raised for unrecoverable system-level errors. Hardware issues, driver crashes, kernel failures, device firmware errors.
Typical causes: Driver bug, corrupt firmware, hardware malfunction, kernel memory access failure, USB port issue.
Recovery: Reinstall drivers, restart machine, update firmware, test with different USB port.
try:
result = camera.get_brightness()
except duvc.SystemError as e:
print(f"System error: {e}")
print(f"Code: {e.error_code}")
print("Try restarting or reinstalling camera drivers")
NotImplementedError - unsupported operations¶
Raised when operation exists in API but isn’t implemented for target camera/platform. Different from PropertyNotSupportedError (property doesn’t exist vs. operation unavailable).
Typical causes: Feature only available on newer firmware, platform limitation, camera model doesn’t support operation.
Recovery: Update camera firmware, use alternative method, check camera specs.
try:
camera.advanced_tracking()
except duvc.NotImplementedError as e:
print(f"Not implemented: {e}")
print("This camera may not support advanced tracking")
BulkOperationError - batch operation diagnostics¶
Raised when setting/getting multiple properties fails partially. Provides detailed failure analysis and per-property error information.
Attributes:
operation(str): Operation name (e.g., “set_properties_batch”).failed_properties(dict[str, str]): Maps property names to error messages.successful_count(int): Number of properties that succeeded.total_count(int): Total properties attempted.
Methods:
get_recovery_suggestions() -> list[str]: Analyzes failure patterns and suggests fixes.
Usage:
props_to_set = {
'brightness': 100,
'contrast': 80,
'pan': 45, # May fail
'focus': 'auto'
}
try:
result = camera.bulk_set_properties(props_to_set)
except duvc.BulkOperationError as e:
print(f"Bulk set failed: {e.operation}")
print(f"Success: {e.successful_count}/{e.total_count}")
# Analyze failures by type
for prop_name, error_msg in e.failed_properties.items():
print(f" {prop_name}: {error_msg}")
# Get intelligent recovery suggestions
for suggestion in e.get_recovery_suggestions():
print(f" → {suggestion}")
# Retry failures individually
for prop, value in props_to_set.items():
if prop in e.failed_properties:
try:
camera.set(prop, value)
except duvc.PropertyNotSupportedError:
print(f"Skipping unsupported: {prop}")
Recovery suggestion algorithm:
“not supported” errors → Group by property, suggest
get_supported_properties()query“out of range” errors → Group by property, suggest
get_property_range()query“busy” errors → Suggest close other apps using camera
Connection errors → Suggest reconnection or driver restart
Mixed errors → Suggest individual retry with detailed error handling
ConnectionHealthError - connection status diagnostics¶
Advanced exception for connection degradation and health monitoring. Indicates connection is functional but experiencing issues.
Attributes:
device_name(str): Camera device name.health_issues(list[str]): List of detected problems (timeout, latency, property locked, etc.).last_working_operation(str or None): Last operation that succeeded, helps narrow scope.
Methods:
get_recovery_suggestions() -> list[str]: Context-aware recovery steps based on issue pattern.
Usage:
try:
camera.health_check()
except duvc.ConnectionHealthError as e:
print(f"Connection degraded: {e.device_name}")
print(f"Issues: {', '.join(e.health_issues)}")
if e.last_working_operation:
print(f"Last working: {e.last_working_operation}")
# Get specific recovery actions
for suggestion in e.get_recovery_suggestions():
print(f" → {suggestion}")
# Recovery pattern: reconnect
try:
camera.reconnect()
except Exception:
# Fallback: full restart
camera.close()
camera = duvc.CameraController(0)
Recovery patterns for system exceptions¶
Timeout/Latency pattern:
Check device still enumerated:
list_devices()Test basic operation:
get_brightness()If no response: attempt
reconnect()If still failing: restart application
Hardware/Driver pattern:
Check Device Manager for errors
Reinstall drivers
Restart system
Try different USB port
Test with USB analyzer if available
Permission pattern:
Check privacy settings (OS level)
Grant app camera permission
Run as administrator if needed
Check user group membership on Windows
Connection loss pattern:
Verify device still connected physically
Check device still in Device Manager
Attempt
reconnect()If persistent: power-cycle device
If still failing: unplug, wait 10s, reconnect
Batch operation pattern:
Identify failure types (unsupported vs. out-of-range vs. permission)
Query capabilities for unsupported properties
Query ranges for out-of-range values
Retry individual properties with corrected values
Log permanently failed properties for future skipping
7.4 Exception Mapping & Recovery¶
Error codes map to specific exception classes through a factory function. Applications catch exceptions at varying specificity levels depending on recovery requirements.
ERROR_CODE_TO_EXCEPTION mapping¶
Complete bidirectional mapping between error codes and exception classes:
Error Code |
Enum Name |
Exception Class |
Attributes |
|---|---|---|---|
0 |
SUCCESS |
(no exception) |
Operation succeeded |
1 |
DEVICE_NOT_FOUND |
|
Device disconnected/unavailable |
2 |
DEVICE_BUSY |
|
Device locked by another process |
3 |
PROPERTY_NOT_SUPPORTED |
|
Property unavailable on device |
4 |
INVALID_VALUE |
|
Value invalid/out of range |
5 |
PERMISSION_DENIED |
|
Insufficient OS permissions |
6 |
SYSTEM_ERROR |
|
Platform/driver failure |
7 |
INVALID_ARGUMENT |
|
Function argument invalid |
8 |
NOT_IMPLEMENTED |
|
Operation not implemented |
create_exception_from_error_code() factory function¶
Internal factory that maps error codes to exception instances. Used automatically by Result types when converting failures to exceptions.
Signature:
def create_exception_from_error_code(
code: int,
message: str,
context: str = None
) -> DuvcError:
"""Create appropriate exception from error code."""
Usage (internal; automatic in Pythonic API):
# Result-based API (explicit code handling)
result = camera.get(duvc.VidProp.Brightness)
if result.is_error():
code = result.error().code()
message = result.error().description()
# Convert to exception if needed
exc = duvc._create_exception_from_error_code(code, message)
raise exc
# Pythonic API (automatic exception raising)
try:
brightness = camera.brightness # Raises exception on error
except duvc.PropertyNotSupportedError:
print("Brightness not available")
Exception patterns with try-except¶
Pattern 1: Broad error handling
try:
camera = duvc.CameraController(0)
camera.set_brightness(150)
except duvc.DuvcError as e:
print(f"Camera error: {e}")
print(f"Error code: {e.error_code}")
print(f"Context: {e.context}")
Pattern 2: Specific exception catching
try:
camera.set_brightness(150)
except duvc.PropertyValueOutOfRangeError as e:
# Out of range: clamp and retry
corrected = max(e.min_val, min(e.max_val, e.value))
camera.set_brightness(corrected)
except duvc.PropertyNotSupportedError as e:
# Property unavailable: use alternative
print(f"Brightness not available on this device")
print(f"Supported: {camera.get_supported_properties()}")
except duvc.PropertyModeNotSupportedError as e:
# Mode not available: use fallback mode
if 'manual' in e.supported_modes:
camera.set_property_manual(e.property_name, 100)
except duvc.DeviceNotFoundError as e:
# Device disconnected: reconnect or restart
camera.reconnect()
except duvc.DuvcError as e:
# Catch-all for other errors
print(f"Unhandled error: {e}")
Pattern 3: Multi-level error handling
try:
# Try primary operation
result = camera.bulk_set_properties(props)
except duvc.BulkOperationError as e:
# Partial success: handle per-property
print(f"Succeeded: {e.successful_count}/{e.total_count}")
for prop, error_msg in e.failed_properties.items():
if "not supported" in error_msg.lower():
print(f"Skip unsupported: {prop}")
elif "out of range" in error_msg.lower():
# Retry with clamped value
try:
camera.set_single_property(prop, props[prop])
except duvc.PropertyValueOutOfRangeError as pe:
corrected = max(pe.min_val, min(pe.max_val, pe.value))
camera.set_single_property(prop, corrected)
except duvc.ConnectionHealthError as e:
# Connection degraded: attempt recovery
print(f"Connection issues: {e.health_issues}")
try:
camera.reconnect()
except Exception:
camera.close()
camera = duvc.CameraController(0)
except duvc.DuvcError as e:
log_error(e)
raise
Specific exception catching strategies¶
Device access errors:
try:
camera = duvc.CameraController(device_index)
except duvc.DeviceNotFoundError:
# List available devices
available = duvc.list_devices()
if not available:
print("No cameras found")
else:
print("Available cameras:")
for dev in available:
print(f" - {dev.name}")
except duvc.DeviceBusyError:
print("Camera in use by another app. Close it first.")
time.sleep(2)
# Retry
camera = duvc.CameraController(device_index)
except duvc.PermissionDeniedError:
print("Camera access denied. Check privacy settings.")
Property value errors:
try:
camera.set_brightness(new_value)
except duvc.PropertyValueOutOfRangeError as e:
# Smart recovery: auto-clamp
corrected = max(e.min_val, min(e.max_val, e.value))
camera.set_brightness(corrected)
print(f"Value clamped from {e.value} to {corrected}")
except duvc.PropertyNotSupportedError as e:
print(f"Property {e.property_name} not available")
# Try alternative property
available = camera.get_supported_video_properties()
print(f"Try: {available}")
except duvc.PropertyModeNotSupportedError as e:
print(f"Mode {e.mode} not available for {e.property_name}")
if e.supported_modes:
fallback = e.supported_modes[0]
camera.set_property_mode(e.property_name, fallback)
System/platform errors:
try:
camera.perform_operation()
except duvc.SystemError as e:
print(f"System error: {e}")
logger.error(f"Unrecoverable system error: {e.error_code}")
# Escalate: requires restart/driver reinstall
raise
except duvc.NotImplementedError:
print("Feature not available on this device")
# Degrade gracefully
use_fallback_method()
Recovery strategies with get_recovery_suggestions()¶
Exceptions with recovery guidance expose suggestions via method:
try:
camera.bulk_set_properties(props)
except duvc.BulkOperationError as e:
suggestions = e.get_recovery_suggestions()
for suggestion in suggestions:
print(f"Try: {suggestion}")
# Apply first suggestion if possible
if suggestions:
if "Reconnect" in suggestions[0]:
camera.reconnect()
elif "Query capabilities" in suggestions[0]:
caps = camera.get_capabilities()
except duvc.ConnectionHealthError as e:
suggestions = e.get_recovery_suggestions()
for suggestion in suggestions:
print(f"Suggested fix: {suggestion}")
# Implement suggestion
if any("reconnect" in s.lower() for s in suggestions):
try:
camera.reconnect()
except Exception:
print("Reconnect failed, restarting...")
camera = duvc.CameraController(0)
except duvc.PropertyValueOutOfRangeError as e:
# Exception message includes suggestion
print(str(e)) # Prints with clamped value suggestion
Recovery suggestion categories:
Error Pattern |
Suggestion Category |
Action |
|---|---|---|
Out of range |
“Try: X” (clamped value) |
Apply directly |
Not supported |
“Query capabilities” |
Call |
Busy/Locked |
“Close other apps” or “Reconnect” |
Specific action provided |
Connection |
“Reconnect” or “Restart device” |
Progressive escalation |
Timeout |
“Check USB connection” |
Hardware/driver check |