FAQ
Answers to the questions that come up most once you're past the quick start, covering runtime validation, cloning, stack traces, and JavaScript-only usage.
No. ErrorKit.Args constrains what you can
pass at the call site, at compile time only. The constructor does a shallow copy
({ ...meta }) with no schema check. If
metadata is crossing a real trust boundary, validate it before it becomes meta. See
Core concepts.
Yes, nothing stops you from passing the same registry literal to two createErrorKit calls.
They will still produce two distinct classes whose instances do not satisfy each other's
instanceof. The isolation comes from the
call to createErrorKit itself, not from the registry object's identity.
Nothing special. It's allowed and common (most registries have several codes at 400 or
404). code is the field designed to be
unique and specific; status only needs to
be a reasonable HTTP status for that code.
No, it's a shallow copy ({ ...meta }).
A top-level key you didn't pass cannot appear later, but a nested object or array inside
meta is still the same reference you
passed in. Mutating that nested object after construction mutates what err.meta holds.
A free-text message is where an interpolated, unreviewed string most often leaks a value
it shouldn't. Making message equal to
code removes that failure mode entirely.
Anything else you want to say goes in meta,
which is scrubbed the same way on every code. See Core concepts.
Not on the kit itself. toJSON is fixed
to { ok: false, error: { code, status, ...scrubbed meta } } .
Add fields (a request id, for example) where you build the HTTP response instead, by
spreading its output. See Extending.
No. .cause is not part of
toJSON()'s output at all. It's the
full-fidelity channel meant for your own logs, not a client response. See
Secret redaction.
A class-per-error-kind hierarchy scales in files (one class per error) and forces catch
sites to chain instanceof checks in the
right order. A registry keeps every code, status, and metadata shape for a subsystem in one
literal, with exhaustiveness the compiler can check. See Core concepts.
Yes, KitErrorImpl extends Error, so
.stack is populated the same way it
would be for any custom Error subclass
on your engine (V8, meaning Node, Bun, and Chromium-based browsers, populates it automatically on
construction).
Yes, typescript is an optional peer
dependency needed only for the types. At runtime this is a small, dependency-free module;
every guarantee described elsewhere in these docs about required metadata, Bare,
and exhaustiveness is compile-time only and simply does not apply without a type checker in
the loop.
If two different versions (or two separately-installed copies) of @gentleduck/error end
up in the dependency tree, which is common in a monorepo or behind certain package manager
hoisting strategies, an error constructed by one copy's createErrorKit call
fails instanceof against a class from the
other copy, even with an identical registry. hasErrorCode exists
specifically to keep working in that situation. See Type narrowing.