Skills· Official

Debugging

Find and fix the root cause of a bug instead of patching symptoms — reproduce it, narrow it down, test one hypothesis at a time, fix the cause, and add a test so it can't come back.

When to use it. When something is broken, a test or check fails for a reason you don't understand, an error shows up in the logs, or a fix didn't hold and the bug came back.

Guessing and patching is how a one-line bug becomes an afternoon. Work the loop below, one variable at a time, and stop when the evidence — not a hunch — points to the cause.

Steps

  1. Read the whole error first. The full message, the stack trace down to its first frame in your code, the input or request that triggered it, and the logs from the same moment. Many bugs are explained right there, before any code changes.
  2. Search what's already known. The exact error text in the codebase, the issue tracker, and the changelog of the dependency involved. In a Gaitro project, run gaitro knowledge search "<error text>" — another agent may have solved it already.
  3. Reproduce it reliably. Find the smallest input or sequence that triggers it every time, and capture it as a command or a failing test. If you can't reproduce it, you can't confirm a fix — gather more evidence (log the failing input, the actual values) before you change anything.
  4. Narrow it down. Halve the search space each time instead of reading everything: which commit introduced it (git bisect), which part of the input triggers it, which layer is wrong (the request, the handler, the query, the stored data).
  5. Test one hypothesis at a time. State it — "the cache returns a stale row because its key leaves out the tenant id" — predict what you'd observe if it's true, then check with one change, one log line, or one assertion. Undo whatever didn't confirm it.
  6. Fix the cause, not the symptom. If a value is wrong, find where it became wrong, not where it was noticed. A try/catch that hides the error, a retry, an extra null check, or a sleep usually moves the bug rather than fixing it.
  7. Prove the fix. The reproduction from step 3 now passes on the new code and still fails on the old, and the full suite passes.
  8. Stop it coming back. Keep the reproduction as a regression test named for the behaviour ("an order's total applies the discount once"). In a Gaitro project, keep it as a project check: gaitro check add "<sentence>" --run "<test command>" --keep.
  9. Look for siblings. The same mistake is often made in more than one place — search for the pattern you just fixed.

When you're stuck

  • Check that the code you're reading is the code that's running: the deployed version, build caches, environment variables, feature flags.
  • Print the actual value instead of trusting the one you expect.
  • Shrink the reproduction further until the cause has nowhere to hide.
  • After two hypotheses fail, write down what you know and what you've ruled out before trying a third.

Done when

  • The bug reproduces reliably on the old code and not on the new
  • The root cause is identified and explained in a sentence or two
  • A regression test fails without the fix and passes with it
  • The full suite passes
  • Temporary debugging code (extra logging, flags, prints) is removed

Report back

What was wrong, in one sentence a non-developer can follow; the root cause and where it lives (file:line); the fix; the test that now guards it; and any similar spots you found.

Traps

  • Changing several things at once, so you can't tell which one fixed it.
  • "Fixing" a flaky test with retries or longer waits.
  • Swallowing the error so the symptom disappears.
  • Calling it fixed without having reproduced it first.

More skills