Diagnostics
Every TT-SDK code the SDKs can emit, what each one means, and what to change when you see it.
On this page
Every message either SDK emits carries a stable TT-SDK-### code. The codes are frozen and
identical across both languages, so a search for one finds the same answer whichever binding
produced it.
This page is the complete list. If you arrived holding a code, the tables below are ordered so the common ones come first.
How they behave
Diagnostics go to a named logger, one per code per process by default. That rate limit exists because these fire on a request path: a dimension value that fails the charset guard fails it on every request, and an unrated warning would be the loudest thing in your logs.
| Setting | Effect |
|---|---|
TOKENTRIAGE_LOG=warn |
the default; one message per code per process |
TOKENTRIAGE_LOG=debug |
lifts the rate limit, so every occurrence prints |
TOKENTRIAGE_LOG=off |
silences everything except the kill-switch line |
Turn debug on when you are diagnosing which requests are affected rather than whether any are.
Two properties are worth knowing. Messages are ASCII, so they survive any console and any CI log.
And TT-SDK-030 prints even at off, because a kill switch that silences itself produces empty
dashboards nobody can explain.
The ones you will actually see
| Code | Means | What to do |
|---|---|---|
TT-SDK-012 |
the response had no x-tt-request-id |
point base_url at the proxy, not the provider |
TT-SDK-013 |
last_receipt() called outside any scope |
read it inside the scope that made the call |
TT-SDK-011 |
an adapter skipped a request off your base_url origin |
nothing, usually; this is the leak guard working |
TT-SDK-001 |
a value was truncated to its byte cap | shorten the value, or accept the truncation |
TT-SDK-008 |
a value or tag key was dropped for its characters | see the charset guard |
TT-SDK-006 |
init() was called after first use |
configure in one place at startup |
TT-SDK-012 is the one that means something is wrong most often. TT-SDK-011 is the one most
often mistaken for a problem: it means an instrumented client made a request somewhere else and
your identity headers correctly stayed at home.
Emission
| Code | Fires when |
|---|---|
TT-SDK-001 |
a value exceeded its byte cap and was truncated |
TT-SDK-002 |
step_class was not in user_turn, tool_loop or provider_state |
TT-SDK-003 |
the per-call cache control was not in off, refresh or no-store |
TT-SDK-004 |
a tag key exceeded 32 bytes and was truncated |
TT-SDK-005 |
traceparent was skipped because the ids were not 32/16 lowercase hex |
TT-SDK-008 |
a value carried a byte outside 0x20–0x7E, or a tag key was outside [a-z0-9_-] |
TT-SDK-010 |
a value was absent or could not be coerced |
TT-SDK-014 |
a non-default tier_header is configured |
All eight drop or trim one header and leave the rest of the request alone. None of them raises.
TT-SDK-014 is a reminder rather than a fault: a renamed tier header is stripped by the proxy
only if your server config names the same header, and if it does not, the header reaches your
provider.
Scopes and context
| Code | Fires when |
|---|---|
TT-SDK-007 |
a trace was opened inside another trace |
TT-SDK-013 |
last_receipt() was called outside any scope |
TT-SDK-018 |
TypeScript only: the runtime has no AsyncLocalStorage |
TT-SDK-018 means traces and steps run but do not propagate, so rows arrive standalone.
Where propagation stops covers what still works.
Adapters
| Code | Fires when |
|---|---|
TT-SDK-009 |
Python only: a CrewAI agent could not carry headers |
TT-SDK-011 |
an adapter declined to inject on a request off the configured origin |
TT-SDK-009 names how many agents of how many were skipped, so a partly instrumented crew is
visible rather than silently partial.
The doctor
| Code | Fires when |
|---|---|
TT-SDK-015 |
check() reports the config-lint result |
TT-SDK-016 |
check() reached the admin plane, and what it disclosed |
TT-SDK-017 |
check() could not verify the admin plane, and why |
TT-SDK-017 carries the reason: unreachable, requires auth, rejected token, an HTTP status, an
invalid URL, or skipped because of a plane collision. With probe=false it says the probe was
disabled rather than that anything failed.
The two that mean something bigger
| Code | Means |
|---|---|
TT-SDK-000 |
an internal fault was caught by the shield |
TT-SDK-030 |
the SDK is turned off by TOKENTRIAGE_DISABLED |
TT-SDK-000 names the entry point and the exception type. It means the SDK hit a bug and returned
its inert value — an empty header map, an all-absent receipt, a no-op scope — instead of raising
into your request path. Your request went out; it may have gone out unlabelled. Please report the
entry point and exception type, since a shielded fault is a defect that has been made survivable
rather than one that has been fixed.
TT-SDK-030 prints once at init, even with logging off:
TT-SDK-030: TokenTriage SDK DISABLED (TOKENTRIAGE_DISABLED) - headers() and adapters are no-ops
While disabled, headers() returns an empty map and adapters are inert. Receipt parsing keeps
working, so reading a response stays safe.
Why the message text is frozen, and the two places it is not
Message text is frozen across languages so that a diagnostic can be searched, alerted on and documented once. Codes never vary at all, which is why every cross-language assertion matches on the code rather than the prose.
Two substitutions are allowed, and only two. A binding spells the API you actually called in its
own convention, so last_receipt() becomes lastReceipt(). And it uses its own literal for
absence, so None becomes null. TT-SDK-010 and TT-SDK-013 are the only affected codes.
The reason it is a carve-out rather than drift: a diagnostic exists to be acted on, and telling a
TypeScript developer that last_receipt() returned None names two things that do not exist in
their program.
Codes 019 through 029 are reserved and unused.