Audit Error in Engine
Bring Try Catch to the Engine level with direct error handling and engine.error() engine.warn() instead of logs
or, better:
Why throw logs(...) is wrong (and why we need engine.error() / engine.fail())
Problem
Using throw logs('error', ...) looks like a shortcut to "log + stop execution", but it breaks error semantics and produces unstable runtime behavior (ghost 500s, missing stacks, inconsistent middleware flow).
logs() is a side-effect, not an error object
logs() exists to emit information (console/audit/file) and possibly trigger a policy action. It is not guaranteed to return an Error.
So this pattern is fundamentally unsafe:
throw logs('error', 'something went wrong', true)
Because throw expects a thrown value that behaves like an error. If logs() returns:
-
undefined→ you effectively dothrow undefined - a
string→ you lose stack trace + structured error handling - a non-Error object →
instanceof Errorfails and error flow becomes inconsistent
Consequences of throwing non-Error values
Throwing a string/undefined/non-Error causes real operational problems:
- No stack trace: you lose
err.stack, so debugging becomes guesswork. - No reliable typing:
err instanceof Errorfails; catch blocks and middleware error handlers cannot distinguish real failures from random thrown values. - Broken middleware flow: frameworks (Express/Koa/Fastify/native adapters) expect
Error-like objects fornext(err)or centralized error handling. - Audit becomes misleading: the log exists, but the engine lacks the real error object that explains where and why the failure happened.
What we actually want
We want one primitive that:
- Captures a real stack trace
- Logs/audits consistently
- Respects engine policies (fatal vs non-fatal)
- Returns or throws a real
Error
Why engine.* must own the trace
Only the engine can enforce consistent error semantics across all packages and adapters.
Expected migration outcome
Packages stop throwing strings or throw logs(...) and instead rely on engine-level error primitives.