Framework Error

Every error the framework throws is built here, so an operator catching one finds the same fields whichever primitive raised it: a name, a stable code like auth/replayed, an isFrameworkError flag and a per-class flag such as isAuthError.

The field that decides what happens next is permanent. A retry layer reads it to tell a failure worth trying again from one that will fail the same way forever: a timeout is transient, a malformed signature is not. Retrying a permanent failure is a loop, and giving up on a transient one is an outage, so each class declares which of its failures are which rather than leaving the caller to guess from a message.

The namespace exports the base class, the three functions that mint classes, and the classes themselves, one per primitive that throws. Operators catch them; only a primitive author mints one.

b.frameworkError.defineMessageFirstClass(name, defaultCode, opts?) #

stable0.20.31
{
  permanent: boolean,   // mark every instance permanent
}

Mint an error class whose constructor takes (message, code), with code falling back to defaultCode when the caller gives none. Use it only to keep a class that was written by hand before the generator existed; a new class uses b.frameworkError.defineClass, whose order matches the rest of the framework.

The class carries factory(code, message), code first, the same as every other class here. That is on purpose: code handed an error class calls the factory and does not have to know which order the constructor takes.

opts.permanent marks every instance permanent, for a class whose failures are never worth retrying.

var b = require("@blamejs/core");
var MyError = b.frameworkError.defineMessageFirstClass("MyError", "my/invalid");
new MyError("that did not parse").code;              // → "my/invalid"
new MyError("that did not parse", "my/bad-input").code;   // → "my/bad-input"
MyError.factory("my/bad-input", "that did not parse").message;
// → "that did not parse"

b.frameworkError.messageFirstFactory(errorClass) #

stable0.20.31

Attach a factory(code, message) to a class whose constructor takes (message, code), and answer the same class.

It exists so a hand-written error class can be passed to code that raises whatever class it was handed. That code calls errorClass.factory(code, msg); without a factory it would call the constructor and put the code where the message goes.

var b = require("@blamejs/core");
class MyError extends b.frameworkError.FrameworkError {}
b.frameworkError.messageFirstFactory(MyError) === MyError;   // → true
typeof MyError.factory;                                      // → "function"

b.frameworkError.defineClass(name, opts?) #

stable0.1.92
{
  alwaysPermanent:     boolean,        // every instance is permanent; arg3 ignored
  withStatusCode:      boolean,        // arg4 is an HTTP status
  withCause:           boolean,        // arg3 is the underlying error
  permanentClassifier: function,       // (code, status) → boolean; arg3 is the status
  transientCodes:      Array,  // these codes are transient, the rest permanent
}

Mint an error class for a primitive. It extends the base class, its constructor takes (code, message, arg3, arg4), and it carries a factory(code, message, arg3, arg4) so code handed the class can raise one without calling new.

An instance carries name, code, message, isFrameworkError, and a flag named after the class, so err.isApiKeyError answers without an instanceof across a module boundary.

What arg3 and arg4 mean is what the options choose, and one option at most:

With none, arg3 is permanent, so each call site says whether that failure is worth retrying. alwaysPermanent makes every instance permanent and ignores arg3, for a class whose failures are all configuration or malformed input. withStatusCode keeps arg3 as permanent and takes an HTTP status in arg4. withCause puts the underlying error in arg3 as cause, for a class that wraps a failure from somewhere else. permanentClassifier takes a status in arg3, keeps it as statusCode, and calls the function with (code, status) to decide permanent, which is how a transport tells a 400 from a 503 without every call site repeating the rule. transientCodes is that same rule when the decision is the code alone: every code it lists is transient and every other code of the class is permanent.

They are mutually exclusive and combining them throws here, at the mint, rather than producing a class whose third argument means two things.

var b = require("@blamejs/core");
var MyError = b.frameworkError.defineClass("MyError", { alwaysPermanent: true });
var err = new MyError("my/bad-input", "that did not parse");
err.code;          // → "my/bad-input"
err.permanent;     // → true
err.isMyError;     // → true

Last updated 2026-10-12T14:21:56.879Z by seeder.