Repository navigation
Error Handling Patterns
SparkEngine uses multiple error handling strategies across its subsystems, chosen based on the boundary type and failure severity. This page documents the conventions, when to use each pattern, and how errors propagate through the engine.
Relevant headers: SparkEngine/Source/Utils/Assert.h, SparkEngine/Source/Utils/Logger.h, SparkEngine/Source/Core/Platform.h
Namespace: Spark
- Philosophy
- Error Handling Strategies
- When to Use What
- Error Logging
- Error Propagation Patterns
- Subsystem Conventions
- See Also
SparkEngine follows these principles for error handling:
- Fail loudly in development, gracefully in production — Assertions catch programmer errors during development; runtime error paths handle user/environment failures
- Validate at system boundaries — Validate user input, file I/O, network data, and API calls. Trust internal engine code
- Return errors, don't throw exceptions — SparkEngine does not use C++ exceptions. All error paths use return values
-
Log before returning — Every error return should be accompanied by a
LOG_ERRORorLOG_WARNso failures are always visible
The most common pattern across the engine. Initialize(), LoadAsset(), and similar functions return true on success:
bool AudioEngine::Initialize()
{
if (!CreateXAudioDevice())
{
LOG_ERROR("Failed to create XAudio2 device");
return false;
}
m_initialized = true;
return true;
}Use for: Engine subsystem initialization, resource loading, operations with a clear pass/fail outcome.
Windows graphics and COM APIs return HRESULT. SparkEngine propagates these directly in graphics code:
HRESULT GraphicsEngine::CreateDevice()
{
HRESULT hr = D3D11CreateDevice(/* ... */);
if (FAILED(hr))
{
LOG_ERROR("D3D11CreateDevice failed: 0x{:08X}", static_cast<uint32_t>(hr));
return hr;
}
return S_OK;
}Use for: D3D11/D3D12 resource creation, shader compilation, any COM interop. Always log the hex HRESULT value for debugging.
For operations that can fail with a meaningful error value, std::expected provides a type-safe result-or-error:
std::expected<Mesh, std::string> LoadMesh(const std::string& path)
{
auto data = ReadFile(path);
if (!data)
{
return std::unexpected("File not found: " + path);
}
Mesh mesh = ParseMeshData(*data);
if (mesh.vertices.empty())
{
return std::unexpected("Empty mesh: " + path);
}
return mesh;
}
// Caller:
auto result = LoadMesh("models/hero.fbx");
if (result)
{
UseModel(*result);
}
else
{
LOG_ERROR("{}", result.error());
}Use for: Operations where the caller needs to know what went wrong, not just that it failed. Preferred for new code over raw bool returns when error context matters.
Assertions catch programming errors — conditions that should never occur if the code is correct:
#include "Utils/Assert.h"
void PhysicsSystem::AddBody(PhysicsBody* body)
{
SPARK_ASSERT(body != nullptr);
SPARK_ASSERT(m_initialized && "PhysicsSystem not initialized");
m_bodies.push_back(body);
}Rules:
- Assertions are enabled in Debug, disabled in Release
- Never put side effects inside an assertion expression
- Use assertions for programmer errors (null pointers, invalid states, violated preconditions)
- Do NOT use assertions for runtime failures (file not found, network error, user input)
Some subsystems use callback-based error reporting, especially for async operations:
// AngelScript compilation errors
scriptEngine.SetMessageCallback([](const std::string& msg, int line)
{
LOG_ERROR("Script error at line {}: {}", line, msg);
});
// Network error handling
networkManager.SetErrorHandler([](NetworkError error, const std::string& detail)
{
LOG_ERROR("Network error {}: {}", static_cast<int>(error), detail);
});Use for: Async operations, third-party library integration, situations where errors are reported to a different context than the caller.
| Situation | Pattern | Example |
|---|---|---|
Subsystem Initialize()
|
bool return |
AudioEngine::Initialize() |
| D3D11/COM API call | HRESULT |
CreateBuffer(), CompileShader()
|
| File/asset loading |
std::expected or bool
|
LoadMesh(), LoadTexture()
|
| Programmer error (impossible state) | SPARK_ASSERT |
Null pointer, uninitialized system |
| Network message parsing |
bool with HasError()
|
NetBuffer::ReadString() |
| Script compilation | Error callback | SetMessageCallback() |
| Configuration validation |
bool + LOG_WARN
|
Invalid settings clamped to valid range |
All errors should be logged through the Logger system:
#include "Utils/Logger.h"
LOG_ERROR("Failed to load texture: {}", path); // Errors (something broke)
LOG_WARN("Texture format unsupported, using fallback"); // Warnings (degraded but functional)
LOG_INFO("Loaded {} textures", count); // Info (normal operation)Guidelines:
-
LOG_ERROR: Something failed and the operation cannot complete -
LOG_WARN: Something unexpected happened but the engine can continue (e.g., fallback used) -
LOG_INFO: Normal operational messages (use sparingly in hot paths) - Include context in the message: file paths, counts, error codes
- Never log inside tight loops — guard with
ifor rate-limit
The predominant pattern. Check each operation and return immediately on failure:
bool Scene::Load(const std::string& path)
{
auto data = ReadFile(path);
if (!data) return false;
if (!ParseHeader(*data)) return false;
if (!LoadEntities(*data)) return false;
if (!LoadLighting(*data)) return false;
return true;
}For operations that should try to complete even with partial failures:
ShaderGraphOutput ShaderGraphCompiler::Compile(const ShaderGraphInput& graph)
{
ShaderGraphOutput output;
if (graph.nodes.empty())
{
output.errors.push_back("Graph has no nodes");
}
// Continue checking...
if (!FindOutputNode(graph))
{
output.errors.push_back("No output node found");
}
output.success = output.errors.empty();
return output;
}Network buffers track an internal error flag:
NetBuffer buf(receivedData);
uint32_t id = buf.ReadUInt32();
std::string name = buf.ReadString();
if (buf.HasError())
{
LOG_WARN("Malformed packet: buffer overflow");
return;
}| Subsystem | Primary Pattern | Notes |
|---|---|---|
| Graphics (D3D11) | HRESULT |
COM convention, always check FAILED()
|
| Physics (Jolt) |
bool + assertions |
Jolt uses assertions internally |
| Audio (XAudio2) | HRESULT |
COM-based API |
| Networking |
bool + HasError()
|
NetBuffer overflow tracking |
| Scripting | Error callbacks | AngelScript message callback |
| ECS | Assertions | Invalid entity/component access |
| Asset Loading |
bool or std::expected
|
File I/O at system boundary |
| Scene Management |
bool early return |
Chain of loading steps |
- Utilities — Logger and Assert implementations
- Testing — How errors are tested
- Profiler and Debugging — Runtime diagnostics
- Contributing — Coding standards for new code
Published from 2b03dc797148. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers