Files
dusk/.claude/errors.md
T
2026-06-16 10:15:59 -05:00

134 lines
3.0 KiB
Markdown

# Error Handling System
Source: `src/dusk/error/`
## Philosophy
Error handling is return-value based. Functions that can fail return
`errorret_t`. There are no exceptions, `errno`, `setjmp`, or global
error codes. Each thread has its own isolated error state.
## Types
```c
typedef uint8_t errorcode_t;
typedef struct {
errorcode_t code;
char_t *message; // allocated; freed by errorCatch
char_t *lines; // call-stack trace; allocated; freed by errorCatch
} errorstate_t;
typedef struct {
errorcode_t code;
errorstate_t *state; // NULL on success
} errorret_t;
```
**Constants:** `ERROR_OK = 0`, `ERROR_NOT_OK = 1`.
Error state is thread-local:
```c
extern THREAD_LOCAL errorstate_t ERROR_STATE;
```
Each thread has its own `ERROR_STATE` so concurrent errors never
interfere.
## Macros
### Throwing an error
```c
errorThrow("message %d", value);
// Throws with ERROR_NOT_OK, captures __FILE__ / __func__ / __LINE__.
errorThrowWithCode(code, "message %d", value);
// Same but with a specific error code.
```
Both macros **return from the current function** with an `errorret_t`.
Do not call them in void functions.
### Propagating up the call stack
```c
errorChain(someCall());
```
If `someCall()` returned an error, appends the current location to the
stack trace and **returns** that error from the current function.
If `someCall()` returned success, execution continues normally.
### Returning success
```c
errorOk();
```
Returns `errorret_t` with `code == ERROR_OK` and asserts the thread's
`ERROR_STATE` is clean (no leftover error). Must be the last statement
in a fallible function.
### Inspecting a result
```c
if(errorIsOk(ret)) { ... }
if(errorIsNotOk(ret)) { ... }
```
### Cleaning up
```c
errorCatch(ret);
```
Frees `ret.state->message` and `ret.state->lines`, resets the thread's
`ERROR_STATE.code` to `ERROR_OK`. Safe to call on a success return
(no-op). **Always call `errorCatch` on errors you are handling** --
otherwise the allocated message and stack-trace leak.
### Logging
```c
errorPrint(ret); // prints code + message + stack trace, returns ret
```
## Typical patterns
### Fallible function
```c
errorret_t myFunction(int_t x) {
if(x < 0) errorThrow("x must be non-negative, got %d", x);
errorChain(someOtherFallibleCall(x));
errorOk();
}
```
### Caller that handles errors
```c
errorret_t ret = myFunction(-1);
if(errorIsNotOk(ret)) {
errorCatch(errorPrint(ret));
// ... fallback logic ...
}
```
### Stack trace accumulation
Each `errorChain()` call appends a line to `ret.state->lines` in the
format ` at file:line in function\n`. A deeply chained error produces
a full call path readable from `ret.state->lines`.
## What NOT to do
- Do not use raw `errno` for in-engine errors.
- Do not return an error code integer -- always return `errorret_t`.
- Do not ignore an error return without calling `errorCatch` on it if
you are not propagating it.
- Do not mix assertions with error handling. Assertions are for
programmer mistakes; `errorThrow` is for expected failure paths.