Instrument a tool failure with an explicit allowlist of operational fields: request reference, tool name, failure category, timing, and correlation ID. Avoid logging the raw exception, headers, or request payload just because a serializer makes that easy.
A timeout can carry more than an error message
An HTTP client exception may include authorization headers, a URL with a token, and a body containing customer records. Replacing visible text in a dashboard leaves those values in the original payload if the logger already transmitted them. Start by selecting what is allowed to leave the process.
Build a useful failure record
- Map known errors to bounded categories such as timeout, denied, invalid-input, or upstream-failure.
- Keep a server-generated request reference that an authorized operator can use to find protected evidence.
- Record durations and retry decisions without including raw input or tool output.
- Test ordinary logs, exception paths, dead-letter records, and exports using synthetic secrets.
Inspect the serialized boundary
The runnable example constructs an exception containing a fake bearer token and a synthetic customer email, then produces an allowlisted record. It checks the actual serialized text, since a clean object shown in a debugger is not enough if another serializer later attaches the original exception.
Limits of an allowlist
Even selected fields need validation. A user-controlled tool name or URL could carry sensitive text, and correlation identifiers can themselves enable linkage. Bound values to known names, restrict access, and retain the minimum necessary evidence. The example does not claim that a general redaction regex can remove every secret.
Run the worked example
Save the synthetic Node.js example and run node instrument-tool-failures-without-secrets.mjs with Node.js 22 or newer. The assertions check the illustrated records and calculations; they do not test a deployed provider, SDK, or access control. No credentials or network connection are needed.
// Synthetic teaching fixture. No credentials, network calls, or production verification.
import assert from 'node:assert/strict';
const failure = {
requestId: 'req-synthetic', tool: 'order_lookup', category: 'timeout', durationMs: 250,
authorization: 'Bearer TEST_SECRET_ONLY', body: { email: 'sample@example.test' },
};
const safe = {
requestId: failure.requestId, tool: failure.tool,
category: failure.category, durationMs: failure.durationMs,
};
const wire = JSON.stringify(safe);
assert.equal(wire.includes('TEST_SECRET_ONLY'), false);
assert.equal(wire.includes('sample@example.test'), false);
assert.equal(safe.category, 'timeout');
console.log(wire);
References and further reading
Maintained by DriftRail. AI assisted the October 2026 content and template revision. Examples are illustrative; they are not measured product benchmarks. Suggest a correction.