Odile Labs

The Guardrails · one of twenty

Free · the whole skill

Unmask before theorising

A system almost never fails without knowing why. The reason is produced, and then thrown away one frame later by code that summarises, catches, prefers the wrong stream, or records a partial success as a total failure. What reaches you is a category.

The cost is not the outage. The cost is the hours spent on a theory the message invited, which is usually wrong, and which feels productive the whole time. On 4 September 2026 this shape produced seven separate multi-hour investigations in one day, across two unrelated systems. Every fix was one to three lines.

The rule. Fix the masking before forming the theory. It is usually one line and it usually ends the investigation.

When to reach for it

  • An error names a category, not a causeError executing tool X, 400 Bad Request, exit 1, no usable token
  • A count is zero or a status is generic — and you are about to explain why
  • Two systems disagree about the same fact — and you are about to pick a side
  • A retry loop exists — and nobody can say what it is retrying against

Do not reach for it when the error already names a specific, actionable cause, or for something you just watched fail in the foreground.

The seven places it hides

Check these in order. Each one was a real instance.

  1. A handler that catches and re-raises a summary The body of the exception is discarded and replaced with its class name.
  2. raise_for_status() before .json() The server sent a JSON error explaining the rejection; raising on the status code throws it away unread.
  3. stderr or stdout precedence A library logs a generic WARNING to stderr while the real structured error sits on stdout. The or chain prefers the noise.
  4. A partial success recorded as a total failure An upload succeeded and returned an id; a later cosmetic step raised; the whole record became an error and the id was lost.
  5. A returned reason the caller ignores The function computes what failed and why; the caller collapses all of it to one word.
  6. A bare except: return None A missing dependency, a revoked credential and a malformed file become the same four words.
  7. The wrong log The record exists in a second file. Concluding “no record exists” from one file produces a confident, wrong root cause.

The procedure

  1. Find where the reason dies. Read from the point of failure outward, not from your hypothesis inward. Grep the generic string — it is often a library’s, not yours.
  2. Make it speak, and only that. Change nothing about behaviour in the same edit. A diagnostic change should be purely additive.
  3. Prove it fires. A guard that has not run looks exactly like a guard that passed. Trigger the failure again and read the new message.
  4. Now theorise. Usually you will not need to.

You are done when

  • The new message names a cause a person could act on, not a category
  • It was observed firing on a real failure, not only reasoned about
  • Behaviour is unchanged: the diff is additive, or replaces only the message
  • The same masking was checked in the sibling path — these land in one code path and not its twin more often than not

Stop if you catch yourself saying

  • “It’s probably a permissions issue” — before reading the body
  • Explaining a zero without checking whether the counter can see the thing
  • A retry loop whose stop condition is a string match on a message you have never read
  • Building a fix for the cause you inferred from a message that names no cause

That was one of twenty

The other nineteen are the same shape: a rule that is left over from something that actually went wrong. Nine more about reasoning, six about money, four about changing a system while people are using it.

Get the pack $29 once

Instant download. No account, no subscription, nothing renews. Or copy the one above into ~/.claude/skills/ and keep it — that is the point of publishing it.