Debugging is a repeatable investigation
When a program behaves incorrectly, first reproduce the problem, reduce it to the smallest useful case and inspect the state that leads to the wrong result. A traceback is often the first important clue when an exception occurs.
Read tracebacks intelligently
The final line normally identifies the immediate exception, while the preceding frames show the calls that led there. The goal is not merely to fix the final line but to understand why the invalid state was produced.
Use tools appropriately
Logging is useful for persistent diagnostics, a debugger is useful for interactive inspection, and automated tests are useful for proving that a fixed bug does not return.
Practice: take an intentional off-by-one bug, reproduce it, identify the incorrect assumption, fix it and add a regression test.