Error catalog

Every trustmint factory-surface error is a stable THM-NNNNN code returned in an application/problem+json body. This page is a front door — the authoritative, machine-readable catalog lives in the repo and is generated, so this page links it rather than copying it.

Source of truth (do not hand-edit)

Artifact

Use

thingmake-error-catalog.md

Human-readable code reference (every code: status, title, description)

thingmake-error-catalog.json

Machine-readable form — what the SDK typed-error surface is generated from

Both are generated from services/thingmake/internal/api/errorTable by services/thingmake/cmd/error-catalog-gen. The CI drift-gate rejects any hand-edit. If you think a code is wrong, fix the source table — not the catalog.

The THM-NNNNN code scheme

Codes are bucketed by domain: the leading digits identify the bucket, so THM-2xxxx is factory-auth, THM-5xxxx is the state-changes feed, and so on. The authoritative bucket list is the catalog’s Reservations table — buckets are generated and the set grows, so read the catalog rather than memorizing a count.

At the time of writing the catalog reserves these domain buckets:

Bucket

Domain

Cross-reference

1

foundation-auth

JWT bearer auth on registration

2

factory-auth

AK/SK signing on registration

3

registrar-lifecycle

registrar approval / provisioning runs

4

thing-state-machine

illegal-transition / wrong-state (Thing lifecycle)

5

state-changes-feed

cursor polling on webhooks

6

subscriptions

webhook subscription rules on webhooks

7

identity-blacklist

the /provisioning/v1/identity-blacklist* endpoint group

8

trust-anchor

trust-anchor delivery to the factory backend

9

internal-cross-domain-reserved

reserved (no public codes yet)

Bucket 2 — factory-auth covers inbound dispatch credential verification on every non-registration call (see registration). The dispatch credential is a Phase 45 workload-identity Bearer (an RS256 JWT carrying iss, aud, exp and a kid), verified against the DotID token-exchange JWKS — it is no longer an FGAI-HMAC-SHA256 signature. The codes are unchanged; what each one now means is:

Code

Dispatch failure mode

THM-20110

MISSING_HEADER — no Authorization header at all

THM-20120

INVALID_SIGNATURE — the token does not verify, or does not bind here (aud is another factory, iss is not the realm, the signing algorithm is not an allowed RSA variant)

THM-20130

EXPIRED — exp is in the past beyond the verifier’s clock-skew leeway

THM-20140

UNKNOWN_KEY — the presented kid resolves to no key in the trusted JWKS. (This is what an unregistered access-key id became: same fact about the caller — you presented a key I do not know — so it keeps the same code.)

THM-20150

REPLAY — NOT EMITTED on the dispatch face. See below.

⚠️ THM-20150 no longer applies to inbound dispatches, and its absence is deliberate. A Bearer is replayable by construction: it authorises many calls to its audience and resource for its whole lifetime, so a legitimate second dispatch and a replay are the same bytes in the same position and no predicate separates them. Refusing on reuse would refuse valid traffic. Replay is therefore bounded rather than refused — short token TTL plus aud/resource binding — and reuse is recorded (not rejected) for audit. Do not treat a successful re-send of the same token as a contract violation, and do not expect THM-20150 on this path. The code remains in the catalog; it is simply not produced here.

THM-20100 (factory-auth-failed) is the generic fallback for an unclassified verification failure. These codes are stable; this mapping is a navigation aid — the authoritative descriptions live in the generated catalog.

Two codes in this bucket are 503, not 401, because your credential was never evaluated. Do not re-sign in response to either:

Code

Meaning

What to do

THM-20210

the signature verifier could not be reached

retry after a short backoff

THM-20220

the service’s own credential to the verifier was refused

report it — this is a server-side defect, not yours

Bucket 7 — identity-blacklist backs the /provisioning/v1/identity-blacklist* endpoint group (see the API reference). Its codes cover blacklist lookups (THM-70100), delta-cursor handling (THM-70110 malformed, THM-70120 older than 72h → full-sync), and snapshot-not-ready (THM-70130).

Bucket 8 — trust-anchor backs trust-anchor delivery on the ACTIVATE saga leg. Its codes are THM-TRUST-ANCHOR-FETCH-FAILED (503), THM-TRUST-ANCHOR-MALFORMED (502), and THM-TRUST-ANCHOR-REJECTED (422). See Trust-anchor delivery §5.

This table is a navigation aid, not the source of truth: if a bucket is added upstream it appears in the catalog’s Reservations table first. Always confirm against thingmake-error-catalog.md.

How the SDKs use it

Both SDKs ship a generated typed-error surface built from thingmake-error-catalog.json. On any 4xx/5xx they parse the problem body, look up the THM-NNNNN code, and return a typed error (errors.Is(err, thingmake.ErrCode...) in Go, except ...Error in Python). See the SDK quickstart.