Skip to main content

libobs_wrapper\utils/
error.rs

1use std::fmt::Display;
2
3use crate::enums::ObsResetVideoStatus;
4
5/// Error type for OBS function calls.
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub enum ObsError {
8    /// The `obs_startup` function failed on libobs.
9    Failure,
10    /// This error is emitted if a dummy DLL was loaded instead of the real libobs DLL. Make sure you bootstrap properly with libobs-bootstrapper
11    InvalidDll,
12    /// Failed to lock mutex describing whether there is a
13    /// thread using libobs or not. Report to crate maintainer.
14    MutexFailure,
15    /// Some or no thread is already using libobs. This is a bug!
16    ThreadFailure,
17    /// Unable to initialize/reset the OBS audio subsystem.
18    ResetAudioFailure,
19    /// Unable to reset video.
20    ResetVideoFailure(ObsResetVideoStatus),
21    /// Unable to reset video because the program attempted to
22    /// change the graphics module. This is a bug!
23    ResetVideoFailureGraphicsModule,
24    /// Unable to reset video because some outputs were still active.
25    ResetVideoFailureOutputActive,
26    /// The function returned a null pointer, often indicating
27    /// an error with creating the object of the requested
28    /// pointer.
29    NullPointer(Option<String>),
30    OutputAlreadyActive,
31    OutputStartFailure(Option<String>),
32    OutputStopFailure(Option<String>),
33    OutputPauseFailure(Option<String>),
34    OutputNotFound,
35    SourceNotFound,
36    SourceNotAvailable(String),
37    InvalidOperation(String),
38    /// Error converting a string between Rust and OBS
39    StringConversionError,
40
41    /// Native error from the Windows API when creating a display
42    DisplayCreationError(String),
43
44    OutputSaveBufferFailure(String),
45
46    /// The obs thread couldn't be called
47    InvocationError(String),
48
49    JsonParseError,
50    /// Couldn't get the sender of the signal
51    NoSenderError,
52    NoAvailableEncoders,
53    /// Error locking a mutex or RwLock. You should probably restart the application to avoid memory leaks.
54    LockError(String),
55    Unexpected(String),
56
57    /// Encoder is still active, stop the attached output before proceeding
58    EncoderActive,
59
60    /// Error during platform-specific initialization
61    PlatformInitError(String),
62
63    /// Error reading/writing to a file
64    IoError(String),
65
66    /// Failed to get data from signal calldata
67    SignalDataError(String),
68
69    /// Failed to convert an enum from a code
70    EnumConversionError(String),
71
72    /// The caller requested metadata using the wrong OBS property category.
73    PropertyTypeMismatch {
74        expected: String,
75    },
76
77    /// A runtime-discovered settings schema does not contain the requested property.
78    PropertyNotFound {
79        name: String,
80    },
81
82    /// A generic property value has the wrong scalar category for the discovered property.
83    PropertyValueTypeMismatch {
84        name: String,
85        expected: String,
86        actual: String,
87    },
88
89    /// A numeric generic property value is outside the range reported by libobs.
90    PropertyValueOutOfRange {
91        name: String,
92        value: String,
93        min: String,
94        max: String,
95    },
96
97    /// A generic value is correctly typed but not accepted by the discovered property metadata.
98    PropertyValueNotAllowed {
99        name: String,
100        value: String,
101    },
102
103    /// The discovered property category is structural/action-oriented and cannot be represented by a scalar setting value.
104    PropertyValueUnsupported {
105        name: String,
106        property_type: String,
107    },
108
109    /// Failed to send/receive on a runtime channel
110    RuntimeChannelError(String),
111
112    /// The bounded OBS actor queue is full.
113    RuntimeQueueFull {
114        capacity: usize,
115    },
116
117    /// The OBS actor panicked while executing a command or cleanup task.
118    RuntimePanicked,
119
120    /// An object from a different OBS runtime/context was passed to this operation.
121    RuntimeMismatch,
122
123    /// A discovered OBS type was used with an incompatible typed creation operation.
124    CapabilityKindMismatch {
125        id: String,
126        expected: String,
127        actual: String,
128    },
129
130    /// A required encoder/service was omitted from a high-level output pipeline.
131    OutputPipelineMissingComponent {
132        output_id: String,
133        component: String,
134    },
135
136    /// A component was supplied to an output that does not accept that component category.
137    OutputPipelineUnexpectedComponent {
138        output_id: String,
139        component: String,
140    },
141
142    /// An encoder codec is incompatible with the selected output type.
143    OutputPipelineUnsupportedCodec {
144        output_id: String,
145        media: String,
146        codec: String,
147    },
148
149    /// A service protocol is incompatible with the selected output type.
150    OutputPipelineUnsupportedProtocol {
151        output_id: String,
152        protocol: String,
153    },
154
155    /// No runtime-discovered output/encoder combination satisfies a high-level request.
156    NoCompatibleOutputGraph {
157        summary: String,
158    },
159
160    /// An audio encoder mixer index exceeded libobs's supported mixer range.
161    AudioMixerIndexOutOfBounds {
162        index: usize,
163        max: usize,
164    },
165
166    /// A blocking operation was requested recursively from the OBS actor thread.
167    RuntimeReentrantBlocking,
168
169    /// Attempted to call a OBS runtime function from outside the OBS thread.
170    /// This error should NEVER occur. If you are not using the runtime manually or have the "enable_runtime" feature enabled
171    /// then please report this to the crate maintainer as this indicates a bug in the crate.
172    RuntimeOutsideThread,
173
174    /// A filter was already applied to a source
175    FilterAlreadyApplied,
176}
177
178#[cfg_attr(coverage_nightly, coverage(off))]
179impl Display for ObsError {
180    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181        write!(f, "OBS Error: ")?;
182
183        match self {
184            ObsError::Failure => write!(f, "`obs-startup` function failed on libobs"),
185            ObsError::MutexFailure => write!(f, "Failed to lock mutex describing whether there is a thread using libobs or not. Report to crate maintainer."),
186            ObsError::ThreadFailure => write!(f, "Some or no thread is already using libobs. This is a bug!"),
187            ObsError::ResetAudioFailure => write!(f, "Could not initialize/reset OBS audio."),
188            ObsError::ResetVideoFailure(status) => write!(f, "Could not reset obs video. Status: {:?}", status),
189            ObsError::ResetVideoFailureGraphicsModule => write!(f, "Unable to reset video because the program attempted to change the graphics module. This is a bug!"),
190            ObsError::ResetVideoFailureOutputActive => write!(f, "Unable to reset video because some outputs were still active."),
191            ObsError::NullPointer(e) => write!(f, "The function returned a null pointer, often indicating an error with creating the object of the requested pointer. Details: {:?}", e),
192            ObsError::OutputAlreadyActive => write!(f, "Output is already active."),
193            ObsError::OutputStartFailure(s) => write!(f, "Output failed to start. Error is {:?}", s),
194            ObsError::OutputStopFailure(s) => write!(f, "Output failed to stop. Error is {:?}", s),
195            ObsError::OutputNotFound => write!(f, "Output not found."),
196            ObsError::DisplayCreationError(e) => write!(f, "Native error from the Windows API when creating a display: {:?}", e),
197            ObsError::OutputSaveBufferFailure(e) => write!(f, "Couldn't save output buffer: {:?}", e),
198            ObsError::SourceNotFound => write!(f, "Source not found."),
199            ObsError::SourceNotAvailable(source_name) => write!(f, "Source {} is not available. See logs or similar to check why.", source_name),
200            ObsError::InvocationError(e) => write!(f, "The obs thread couldn't be called: {:?}", e),
201            ObsError::JsonParseError => write!(f, "Failed to parse JSON data."),
202            ObsError::NoSenderError => write!(f, "Couldn't get the sender of the signal."),
203            ObsError::NoAvailableEncoders => write!(f, "No available encoders found."),
204            ObsError::OutputPauseFailure(s) => write!(f, "Output failed to pause. Error is {:?}", s),
205            ObsError::LockError(e) => write!(f, "Error locking a mutex or RwLock: {:?}. You should probably restart the application to avoid memory leaks.", e),
206            ObsError::Unexpected(e) => write!(f, "Unexpected error: {:?}", e),
207            ObsError::EncoderActive => write!(f, "Encoder is still active, stop the attached output before proceeding"),
208            ObsError::StringConversionError => write!(f, "Error converting a string between Rust and OBS"),
209            ObsError::PlatformInitError(e) => write!(f, "Error during platform-specific initialization: {}", e),
210            ObsError::InvalidOperation(e) => write!(f, "Invalid operation: {}", e),
211            ObsError::IoError(e) => write!(f, "I/O error: {}", e),
212            ObsError::SignalDataError(e) => write!(f, "Signal data error: {}", e),
213            ObsError::EnumConversionError(e) => write!(f, "Enum conversion error: {}", e),
214            ObsError::PropertyTypeMismatch { expected } => write!(f, "OBS property type mismatch; expected {expected}"),
215            ObsError::PropertyNotFound { name } => write!(f, "OBS settings schema has no property named '{name}'."),
216            ObsError::PropertyValueTypeMismatch { name, expected, actual } => write!(f, "OBS property '{name}' expects {expected}, but received {actual}."),
217            ObsError::PropertyValueOutOfRange { name, value, min, max } => write!(f, "OBS property '{name}' value {value} is outside the supported range {min}..={max}."),
218            ObsError::PropertyValueNotAllowed { name, value } => write!(f, "OBS property '{name}' does not accept value {value}."),
219            ObsError::PropertyValueUnsupported { name, property_type } => write!(f, "OBS property '{name}' ({property_type}) is not a scalar setting value."),
220            ObsError::RuntimeChannelError(e) => write!(f, "Runtime channel error: {}", e),
221            ObsError::RuntimeQueueFull { capacity } => write!(f, "OBS runtime queue is full (capacity: {}). Batch work or retry later.", capacity),
222            ObsError::RuntimePanicked => write!(f, "The OBS actor panicked and has been shut down."),
223            ObsError::RuntimeMismatch => write!(f, "The operation mixed objects owned by different OBS runtimes."),
224            ObsError::CapabilityKindMismatch { id, expected, actual } => write!(f, "Discovered OBS type '{id}' has kind '{actual}', but this operation requires '{expected}'."),
225            ObsError::OutputPipelineMissingComponent { output_id, component } => write!(f, "Output pipeline for '{output_id}' requires {component}."),
226            ObsError::OutputPipelineUnexpectedComponent { output_id, component } => write!(f, "Output pipeline for '{output_id}' does not accept {component}."),
227            ObsError::OutputPipelineUnsupportedCodec { output_id, media, codec } => write!(f, "Output '{output_id}' does not support {media} codec '{codec}'."),
228            ObsError::OutputPipelineUnsupportedProtocol { output_id, protocol } => write!(f, "Output '{output_id}' does not support service protocol '{protocol}'."),
229            ObsError::NoCompatibleOutputGraph { summary } => write!(f, "No compatible OBS output graph: {summary}"),
230            ObsError::AudioMixerIndexOutOfBounds { index, max } => write!(f, "Audio mixer index {index} is out of bounds (max {max})."),
231            ObsError::RuntimeReentrantBlocking => write!(f, "A blocking operation cannot wait for an OBS callback from the OBS actor thread."),
232            ObsError::InvalidDll => write!(f, "A dummy DLL was loaded instead of the real libobs DLL. Make sure you bootstrap properly with libobs-bootstrapper"),
233            #[cfg(feature="enable_runtime")]
234            ObsError::RuntimeOutsideThread => write!(f, "Attempted to call a OBS runtime function from outside the OBS thread. This is a bug in the crate!"),
235            #[cfg(not(feature="enable_runtime"))]
236            ObsError::RuntimeOutsideThread => write!(f, "Attempted to call a OBS runtime function from outside the OBS thread. Make sure that you do not use any OBS struct from a different thread than the one where the ObsContext was initialized. THIS BUG WILL CAUSE MEMORY CORRUPTION OR DEADLOCKS!"),
237            ObsError::FilterAlreadyApplied => write!(f, "Filter was applied already."),
238        }
239    }
240}
241
242impl std::error::Error for ObsError {}