Skip to content
AgileLogics
5 min read

Debugging Kotlin

Prerequisites: Testing with kotlin.test and JUnit

Debugging is finding out why code doesn't do what you expect.

1. Read the compiler's message

Many Kotlin bugs are caught at compile time. Read the first error — later ones are often caused by it — and press Alt + Enter on the red underline to see IntelliJ's suggested fixes.

2. Read the stack trace

An uncaught exception prints a stack trace:

Exception in thread "main" java.lang.NullPointerException
    at OrderServiceKt.total(OrderService.kt:12)
    at OrderServiceKt.main(OrderService.kt:20)

Read it top to bottom: the exception type, then where it happened (OrderService.kt, line 12, in total), then who called that (line 20, in main). In IntelliJ, file references are clickable. When the trace includes library code, look for the first line from your code.

3. println — quick and effective

Printing values at key points often finds the problem fast. also lets you print in the middle of a chain without restructuring it:

fun main() {
    val total = listOf(3, 8, 1)
        .filter { it > 2 }
        .also { println("after filter: $it") }
        .sum()
    println(total)
}
after filter: [3, 8]
11

4. The debugger

Click in the gutter next to a line number to set a breakpoint, then run with Debug (the bug icon) instead of Run. Execution pauses at the breakpoint and you can:

  • Inspect every variable in the Variables panel.
  • Step Over (F8) to the next line, or Step Into (F7) a function.
  • Evaluate Expression (Alt + F8) to run any code in the paused context.

Right-click a breakpoint to add a condition (stop only when index == 42) — invaluable for bugs that happen deep inside a loop.

5. check and require

Encode your assumptions so they fail loudly where they break:

fun setVolume(level: Int) {
    require(level in 0..10) { "Volume must be 0-10, got $level" }
    // ...
}

A clear failure at the source is far easier to debug than bad data surfacing somewhere else later.

A debugging process

  1. Reproduce the problem reliably — ideally as a failing test.
  2. Narrow it down: inspect values halfway through, and keep halving.
  3. Question assumptions — "this can't be null" is often exactly the problem.
  4. Fix it and keep the test, so the bug can't come back.