Leveling Up Error Handling: Why We Refactored Flowplayer’s Error System
We’re excited to share a recent improvement to Flowplayer’s architecture that makes error handling more robust, predictable, and developer-friendly! This seemingly small change makes a big difference in real-world applications, so let’s dive into why we made this change and how it benefits your development workflow.
The Challenge: When Error Events Collide
Our previous approach to error handling mapped various errors from different sources (hls.js, WebRTC, etc.) to the browser-native error
event on the HTMLVideoElement
. The challenge? This element already throws its own MediaError
natively.
This created an unfortunate situation where two different types of errors were using the same event, something which the Playback team has been unsatisfied with as a design decision for a very long time:
// Old approach - mapping everything to the native error event
videoElement.dispatchEvent(new ErrorEvent('error', {
error: someCustomErrorObject
}));
// Meanwhile, the browser is also doing this with completely different error structures
videoElement.dispatchEvent(new ErrorEvent('error', {
error: new MediaError(/* browser native error */)
}));
Error handling became unnecessarily complex. Developers had to write extra code to distinguish between different error types with inconsistent structures. Additionally browser vendors implement this event slightly differently, which required a significant amount of domain-knowledge to handle sufficiently, which isn’t in line with the “it just works” experience we strive for!
The Solution: A Custom Event for Better Control
We’ve implemented a cleaner approach by creating a dedicated STANDARD_ERROR
event to handle our own errors without interfering with the browser’s native error events. Think of it as giving each type of error its own lane on the highway!
// New approach - using a custom event for our own errors
videoElement.dispatchEvent(new CustomEvent(flowplayer.events.STANDARD_ERROR, {
detail: {
code: errorCode,
message: errorMessage,
source: errorSource,
// Additional structured data...
}
}));
// Browser still handles its own errors separately
// videoElement.dispatchEvent(new ErrorEvent('error', { ... }));
As a bonus, we’ve also adopted the Streaming Video Technology Alliance’s standardized player error codes (SVTA-2070). Because if there’s one thing developers love more than fixing errors, it’s knowing exactly which error they’re fixing!
Benefits: A Better Developer Experience
- Clear distinction between sources: Developers can easily tell if an error came from the browser or from our player. No more error identity crises!
- Structured error data: Our
STANDARD_ERROR
event includes consistent properties across all error sources, making handling errors across different components much more predictable. - No event collision: We no longer interfere with the browser’s native error handling, creating a cleaner separation of concerns.
- Better debugging: When issues arise (and in video streaming, they certainly will!), developers can more easily identify the source of problems.
- Standardized error codes: By adopting the SVTA-2070 specification, our errors follow industry best practices, making knowledge transferable across different video players.
Implementation Example
Here’s how a developer might handle these different errors with our new approach:
// Handle browser native errors
// These are also mapping to `flowplayer.events.STANDARD_ERROR`
// so all of your error handling can be done in one place.
videoElement.addEventListener('error', (event) => {
const mediaError = event.target.error;
console.error('Browser native error:', mediaError.code, mediaError.message);
});
// Handle Flowplayer errors - using the enum instead of a string literal
videoElement.addEventListener(flowplayer.events.STANDARD_ERROR, (event) => {
const { code, message, resource } = event.detail;
console.error(`error from ${resource}:`, code, message);
// SVTA error codes are categorized for easier handling
// Since the error codes are strings, we use string comparison
if (code.startsWith('1')) {
return console.error("Network-related error detected");
}
if (code.startsWith('4')) {
return console.error("DRM error detected");
}
});
Conclusion
This refactoring represents our ongoing commitment to improving the developer experience with Flowplayer. While it might seem like a small change, these kinds of thoughtful architectural decisions add up to make development smoother and more enjoyable.
By separating our error events from native browser events and adopting industry standards, we’ve made debugging easier and error handling more consistent. It’s part of our philosophy that the best code is not just powerful—it’s also a pleasure to work with.
Next time you’re implementing error handling with Flowplayer, you’ll hopefully appreciate this little quality-of-life improvement. After all, in video streaming, errors are inevitable—but dealing with them doesn’t have to be painful.