Skip to content
wiki edited this page Sep 4, 2026 · 1 revision

Errors

The sentinels an extension branches on. They live in this module, not in rex, because an extension depends on the contract and not on the framework — a sentinel declared only in rex is unreachable from the code that needs to match it.

Before these existed, extensions were reduced to strings.Contains(err.Error(), "already exists"). That appeared in both rextension-health and rextension-metric, coupling each of them to the exact wording of another module's error string.

rex aliases all three, so errors.Is matches across either import path.

ErrRouterExists

CreateRouter was called for a name already taken.

Usually not a failure. Two extensions may both want a router called metrics, and whichever runs second should reuse it rather than abort startup. The standard shape:

if err := r.CreateRouter(name, cfg); err != nil &&
	!errors.Is(err, rextension.ErrRouterExists) {
	return err
}

Note that the first extension's configuration wins. If both pass a RouterConfig, the second one's is discarded — which is the right default, but worth a log line if your extension cares about its limits.

ErrRouterUnknown

A route or middleware named a router that was never created.

Reported when the route table is built, not at the registration call, because whether a router exists depends on every extension having had its turn to create one. The error is wrapped with the method, path and call site of the registration that named it, so the source of a typo is in the message.

ErrRouterFrozen

A route or middleware was registered after the route table was built.

The table is published by atomic swap and read without a lock, so there is nowhere safe to add to it. Register from OnInitialize or OnStartnever from OnReady.

If you hit this while upgrading, an OnReady hook is almost certainly the cause; move the registration to OnStart.

Framework-side sentinels

These live in rex and are not aliased here, because extension code has no occasion to match them:

rex.ErrAlreadyRunning a second call to Run
rex.ErrRouterNotFrozen a router was started before its table was built
rex.ErrNilRoute a nil route, or one with no handler

Container errors

Resolution and registration failures come from dixErrNotRegistered, ErrScopedFromRoot, ErrAmbiguousResolution, ErrAlreadyRegistered, ErrInvalidFactory, ErrInvalidTarget, ErrScopeClosed.

An extension that wants to branch on one does have to import dix for the sentinel. In practice most extension code does not: a failed resolve during OnInitialize should abort startup, and a failed resolve in a handler should answer with ProblemInternal. Neither needs to know which failure it was.

Clone this wiki locally