skip to content

Result & Error Handling

Kotlin's approach to failure: exceptions are all unchecked, Result gives you a value-based alternative, and require/check express preconditions. Interviewers use this area to see how you draw the line between expected outcomes and bugs.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

explore

questions

20

Does Kotlin have checked exceptions? Do you need to declare or catch the exceptions a function might throw?

level: juniorimportance: must knowfreq 78%

answer

  1. All Kotlin exceptions are unchecked
  2. No 'throws' keyword in signatures
  3. @Throws only for Java interop
  4. Compiler never forces try/catch
  5. Design choice: avoid boilerplate catch

basics

~10 s

No. In Kotlin every exception is unchecked. You are never forced to catch an exception or list it in the function signature. There is no 'throws' clause like in Java.

solid answer

~40 s

Kotlin has no checked exceptions. Every Throwable is treated as unchecked, so the compiler never forces you to wrap a call in try/catch or declare what it throws. There is no `throws` keyword in a function signature. This is a deliberate language decision: Roman Elizarov and the Kotlin team argued checked exceptions in large codebases lead to boilerplate `catch` blocks that swallow or rethrow. You can still catch anything you like with `try/catch`. When calling Kotlin from Java, you add the `@Throws(IOException::class)` annotation so the generated Java method declares the checked exception, satisfying Java callers. Without it, Java code that needs the declaration won't compile against your Kotlin API. The annotation changes only the generated bytecode signature; it has no effect on Kotlin callers.

go deeper

for a junior

Knows there are no checked exceptions and try/catch is optional.

for a middle

Explains the design rationale and mentions @Throws for interop.

for a senior

Articulates the bytecode-signature effect of @Throws and when Java callers break without it.

for a principal

Discusses API-design tradeoffs of unchecked-only exceptions and team conventions for documenting throwable failures.

## Checked vs unchecked exceptions In Java, exceptions are split into two groups: - **Checked exceptions** (subclasses of `Exception` but not `RuntimeException`, e.g. `IOException`): the compiler *forces* the caller to either catch them or declare them with `throws` in the method signature. - **Unchecked exceptions** (subclasses of `RuntimeException` and `Error`): no compiler enforcement. ## Kotlin's rule: everything is unchecked Kotlin treats **all** exceptions as unchecked. The compiler never requires you to: - wrap a call in `try/catch`, or - declare thrown types in the function signature (there is no `throws` keyword in Kotlin at all). This means code like reading a file compiles with no ceremony: ```kotlin fun read(path: String): String = java.io.File(path).readText() // can throw IOException, no catch required ``` ## Why this design The Kotlin team's position is that checked exceptions, in large codebases, encourage low-quality `catch` blocks that either swallow the exception or blindly rethrow it, adding boilerplate without improving reliability. So Kotlin removed the enforcement while keeping the `try/catch` machinery for when you genuinely want to handle a failure. ## Interop: `@Throws` Because Java still cares about checked exceptions, Kotlin provides the `@Throws` annotation. It does not change Kotlin semantics; it only adds a `throws` clause to the **generated Java bytecode signature** so Java callers can (and must) handle it: ```kotlin @Throws(java.io.IOException::class) fun loadConfig(path: String): String = java.io.File(path).readText() ``` Without `@Throws`, Java code calling `loadConfig` and surrounding it with `catch (IOException e)` will fail to compile, because Java thinks the method never throws a checked exception. ## Key terms - **Throwable**: the root type of everything you can `throw` or `catch`. - **Unchecked**: not enforced by the compiler. - **`@Throws`**: interop-only annotation for Java callers.

  • If exceptions are unchecked, how does a Java caller know to catch a Kotlin function's IOException?
    It doesn't, unless you annotate the Kotlin function with @Throws(IOException::class), which emits a Java 'throws' clause in the bytecode signature.
  • Does @Throws change behavior for Kotlin callers?
    No. Kotlin callers are still never forced to catch it; @Throws affects only the generated Java signature.

Java's checked exceptions are like a form you must fill in at every door; Kotlin removes the form but the doors are still there if you choose to use them.

saying these in an interview costs you the question

  • Saying Kotlin has checked exceptions like Java
  • Claiming the compiler forces you to catch IOException
  • Thinking @Throws makes Kotlin callers catch the exception
  • Believing there is a 'throws' keyword in Kotlin signatures

context

open as a page

What is the difference between require(...) and check(...) in the Kotlin standard library, and which exception does each throw?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Both check a condition and throw if it is false. require is for bad arguments and throws IllegalArgumentException. check is for a bad object state and throws IllegalStateException.

open as a page

What is Kotlin's Result<T> type and what does runCatching { } do?

level: juniorimportance: must knowfreq 60%

basics

~10 s

Result<T> is a box that holds either a successful value or a failure with an exception. runCatching runs a block of code and returns success if it finishes, or failure if it throws.

open as a page

Given a Kotlin Result<T>, what are the differences between getOrNull(), getOrDefault(), getOrElse(), and getOrThrow() for extracting the value?

level: juniorimportance: must knowfreq 70%

basics

~10 s

All four pull the success value out of a Result. getOrNull returns null on failure, getOrDefault returns a fallback you pass, getOrElse computes a fallback from the exception, and getOrThrow re-throws the failure's exception.

open as a page

Which runtime exception types does the Kotlin stdlib throw for common operations, and when is each raised?

level: middleimportance: must knowfreq 64%

basics

~20 s

The stdlib throws specific runtime exceptions: bad numeric text gives NumberFormatException, a null in a non-null spot gives NullPointerException, a bad index gives IndexOutOfBoundsException, an empty collection's first() gives NoSuchElementException, and a wrong cast gives ClassCastException.

open as a page

How does try/catch behave as an expression in Kotlin? What value does it produce, and how does finally interact with that value?

level: middleimportance: must knowfreq 70%

basics

~20 s

In Kotlin try/catch can return a value. The result is the last expression of whichever block runs — the try block if it succeeds, or the matching catch block if it fails. A finally block runs but does not change the returned value.

open as a page

How do requireNotNull and checkNotNull work, and what do they do to the type of the value afterwards?

level: middleimportance: must knowfreq 60%

basics

~10 s

They throw if the value is null, otherwise they return the same value as a non-null type. So after calling them you can use the value without a null check.

open as a page

What does Result.fold do, and how does it differ from chaining getOrElse with onSuccess/onFailure?

level: middleimportance: must knowfreq 60%

basics

~20 s

fold takes two lambdas — one for the success value and one for the failure exception — and returns a single result by running whichever branch matches. It collapses both outcomes into one value in one call.

open as a page

What is the cancellation hazard when using runCatching inside a coroutine, and how do you handle it correctly?

level: seniorimportance: must knowfreq 35%

basics

~10 s

runCatching catches every exception, including the special one that signals a coroutine was cancelled. Swallowing that breaks cancellation, so you must re-throw CancellationException instead of treating it like a normal failure.

open as a page

Describe Kotlin's Throwable hierarchy. What are Throwable, Exception, RuntimeException, and Error, and which should you typically catch?

level: middleimportance: should knowfreq 58%

basics

~20 s

Throwable is the root of everything you can throw or catch. Exception and Error are its two children. Exception (and its child RuntimeException) means recoverable problems; Error means serious JVM failures you normally let crash. Catch Exception, not Throwable.

open as a page

What does error(message) do, and why do require/check use a lazy message lambda instead of a plain String parameter?

level: middleimportance: should knowfreq 45%

basics

~10 s

error(msg) always throws an IllegalStateException with that message and never returns. The lazy lambda means the message is only built when the check actually fails, so it costs nothing when everything is fine.

open as a page

What is the difference between the top-level runCatching { } and the receiver form someObject.runCatching { }, and how do you read the captured exception?

level: middleimportance: should knowfreq 45%

basics

~10 s

Top-level runCatching just runs a block. The receiver form runs the block with a chosen object as 'this' inside it. Both return a Result; you read the error with exceptionOrNull() or getOrElse.

open as a page

When should you choose runCatching over a plain try/catch, and what are the trade-offs?

level: middleimportance: should knowfreq 38%

basics

~20 s

Use runCatching when you want the outcome as a value you can pass around, transform with map/fold, or chain. Use try/catch when you only need to react to specific exceptions in place. runCatching catches everything, which is sometimes too broad.

open as a page

What is the difference between Result.map and Result.mapCatching, and when must you choose mapCatching?

level: middleimportance: should knowfreq 50%

basics

~20 s

Both transform the success value with your lambda and leave failures alone. The difference: if your lambda itself throws, map lets the exception escape, while mapCatching catches it and turns the Result into a failure.

open as a page

What is the type of a 'throw' expression in Kotlin, and how does the Nothing type make throw usable in places that expect a value?

level: seniorimportance: should knowfreq 52%

basics

~20 s

A throw expression has type Nothing, the type with no values that is a subtype of every type. Because it fits anywhere, you can use throw on the right side of ?: (Elvis), as a when branch, or as a single-expression function body.

open as a page

How does Kotlin's assert function differ from require/check, and when is it actually evaluated on the JVM?

level: seniorimportance: should knowfreq 35%

basics

~20 s

assert checks an internal sanity condition but can be turned off. On the JVM it only runs when assertions are enabled with the -ea flag. require and check always run. So never use assert to validate real input.

open as a page

How do the Kotlin contracts behind require/check/requireNotNull enable smart-casting, and how would you choose the right precondition function when designing an API?

level: seniorimportance: should knowfreq 30%

basics

~20 s

These functions tell the compiler, via contracts, what is guaranteed if they return without throwing. That lets the compiler narrow types (e.g. to non-null or to a subtype). When designing APIs, pick require for bad inputs, check for bad state, and error for unreachable code.

open as a page

Why is Result<T> discouraged as a public function return type, and what alternatives are recommended?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Result is meant for internal capture, not as a return type. It is opaque about which errors can occur, it boxes when used generically, and the team that designed it advises returning your own success/failure type or throwing instead.

open as a page

What does Result.recover (and recoverCatching) do, and how is it the dual of map?

level: seniorimportance: should knowfreq 40%

basics

~20 s

recover turns a failed Result back into a success by running a lambda on the exception to produce a fallback value. Successes pass through untouched. recoverCatching is the same but catches exceptions thrown inside that recovery lambda.

open as a page

You are building a Result transformation pipeline (map/mapCatching/recover/fold) inside suspend functions. What correctness and design pitfalls should you guard against?

level: principalimportance: nice to knowfreq 25%

basics

~10 s

The big one: mapCatching/recoverCatching catch all Throwable, including CancellationException, so a Result pipeline can silently swallow coroutine cancellation. Always re-throw CancellationException. Also avoid hiding domain errors behind a generic Result that loses type information.

open as a page