Real examples of type errors in Kotlin: examples and fixes
Kotlin’s type system is strict on purpose. It prevents whole classes of runtime bugs, but that safety shows up as compile‑time type errors. Let’s walk through some of the best examples of type errors in Kotlin, along with fixes you can actually reuse.
You’ll see that most compiler messages fall into patterns:
- Type mismatch between expected and actual type
- Nullability conflicts (
TvsT?) - Smart cast not allowed
- Generic type mismatch
- Variance (
in/out) issues - Platform type surprises from Java interop
Each section below contains at least one real example of a type error, the message you’re likely to see, and a practical fix.
Example 1: Classic Type mismatch between Int and Long
This is one of the simplest examples of type errors in Kotlin, but it still bites people migrating from Java.
fun totalUsers(): Long {
val active: Int = 120
val inactive: Int = 5
// Type mismatch: inferred type is Int but Long was expected
return active + inactive
}
The compiler sees active + inactive as an Int, but the function promises to return a Long.
Fix options:
You can either change the function return type to Int or promote the expression to Long.
fun totalUsers(): Long {
val active = 120
val inactive = 5
return (active + inactive).toLong()
}
Or, if you’re working with large counts and really want Long everywhere:
fun totalUsers(): Long {
val active: Long = 120L
val inactive: Long = 5L
return active + inactive
}
The pattern: whenever you see a simple Type mismatch error, compare the function signature, variable types, and literal suffixes (L for Long, f for Float).
Example 2: Nullability mismatch – assigning String? to String
Kotlin’s null‑safety is one of its biggest selling points and one of the most common sources of type errors.
fun greet(name: String?) {
// Type mismatch: inferred type is String? but String was expected
val upper: String = name.uppercase()
println("Hello, $upper")
}
The compiler is yelling because name is String?, so name.uppercase() is also String?. You’re trying to assign that to a non‑nullable String.
Better fix: handle the null case explicitly.
fun greet(name: String?) {
val upper: String = name?.uppercase() ?: "ANONYMOUS"
println("Hello, $upper")
}
Or, if you want to fail fast instead of defaulting:
fun greet(name: String?) {
val upper: String = name?.uppercase()
?: error("Name must not be null")
println("Hello, $upper")
}
If you’re absolutely certain it can’t be null at this point, you can use !!:
val upper: String = name!!.uppercase()
…but that turns a compile‑time type error into a potential runtime NullPointerException. Use it sparingly.
This is one of the best examples of type errors in Kotlin: examples and fixes that clearly show how the type system forces you to think about nulls.
Example 3: Smart cast error – variable can be changed by another thread
Smart casts are Kotlin’s way of letting you write code that feels dynamic but is still statically checked. They don’t always work, though.
fun printLength(text: CharSequence?) {
if (text is String) {
// Smart cast to 'String' is impossible, because 'text' is a mutable property that could be changed by another thread
println(text.length)
}
}
You’ll see this sort of error when:
- The variable is not
val - Or it’s a property with a custom getter
- Or it’s captured from a closure where the compiler can’t prove it won’t change
Fix: create a local val inside the if block.
fun printLength(text: CharSequence?) {
if (text is String) {
val s: String = text
println(s.length)
}
}
Or use when with a smart cast‑friendly pattern:
fun printLength(text: CharSequence?) {
when (text) {
is String -> println(text.length)
null -> println("No text")
else -> println("Not a String")
}
}
This kind of smart cast issue is a subtle example of a type error in Kotlin that shows up more often in concurrent or Android UI code.
Example 4: Generic type mismatch – List<String> vs List<Any>
Generics are where many of the trickier examples of type errors in Kotlin live.
fun addItem(target: MutableList<Any>) {
target.add(42)
}
fun main() {
val names: MutableList<String> = mutableListOf("Alice", "Bob")
// Type mismatch: inferred type is MutableList<String> but MutableList<Any> was expected
addItem(names)
}
In Java, this might compile thanks to raw types (and then blow up later). Kotlin’s type system doesn’t allow passing MutableList<String> where MutableList<Any> is expected.
Why? If it were allowed, addItem could insert an Int into a list of String, breaking type safety.
Fix options:
- Change the function to be generic:
fun <T> addItem(target: MutableList<T>, item: T) {
target.add(item)
}
fun main() {
val names = mutableListOf("Alice", "Bob")
addItem(names, "Carol")
}
- Or narrow the parameter type to what you actually need:
fun addName(target: MutableList<String>, name: String) {
target.add(name)
}
This is a textbook example of type errors in Kotlin: examples and fixes that come directly from Kotlin’s stricter generics compared to Java.
Example 5: Variance (out / in) and Type mismatch
Variance annotations (out, in) are another source of confusing compiler messages.
class Box<out T>(val value: T)
fun takeAnyBox(box: Box<Any>) { /* ... */ }
fun main() {
val stringBox: Box<String> = Box("Hello")
// Type mismatch: inferred type is Box<String> but Box<Any> was expected
takeAnyBox(stringBox)
}
You’d expect Box<String> to be usable where Box<Any> is expected, because String is a subtype of Any. But Kotlin doesn’t automatically infer the variance here.
Fix: use a covariant type parameter in the function instead of Box<Any>.
fun takeAnyBox(box: Box<*>) { /* read‑only */ }
// or
fun <T> takeAnyBox(box: Box<T>) { /* read‑only */ }
If you only read from box, use a star projection (Box<*>) or a covariant out parameter on the class as we did. If you need to both read and write, you probably want in on the parameter type instead.
Kotlin’s official docs have a solid explanation of variance with more examples: https://kotlinlang.org/docs/generics.html
Example 6: Platform types and Java interop surprises
When you call Java code from Kotlin, you get platform types – types where the nullability isn’t known at compile time. This is one of the more subtle real examples of type errors in Kotlin.
Imagine a Java API:
// Java
public class UserApi {
public String getUsername() { return null; }
}
From Kotlin:
val api = UserApi()
// 'username' is a platform type: String! (unknown nullability)
val username: String = api.username
// This may compile but can crash at runtime with NPE
The compiler allows this assignment, but it’s not actually safe. When you start adding nullability annotations in Java (@Nullable, @NotNull), Kotlin’s type system gets better information and you see more explicit type errors instead of runtime crashes.
Better pattern in Kotlin:
val username: String? = api.username
val safeUsername: String = username ?: "guest"
Or, if you control the Java code, annotate it with something like org.jetbrains.annotations.NotNull or @Nullable so Kotlin can infer the right type and give you real compile‑time errors.
For background on nullability annotations and static analysis, the U.S. National Institute of Standards and Technology (NIST) has general guidance on safer software practices: https://samate.nist.gov/
Example 7: Nothing and non‑returning functions
Nothing is Kotlin’s bottom type. It represents a value that never exists because the function never returns.
fun fail(message: String): Nothing {
throw IllegalStateException(message)
}
fun computeOrFail(flag: Boolean): Int {
if (!flag) {
// Works: fail() returns Nothing, which is a subtype of Int
return fail("Flag must be true")
}
// Type mismatch: inferred type is Nothing but Int was expected
return fail("Unreachable")
}
The first return fail(...) is fine, because Nothing can stand in for any type. The second one is misleading: the function already returns from the fail call, so the compiler may complain depending on control‑flow analysis.
Cleaner fix: just call fail without return when it’s the last statement.
fun computeOrFail(flag: Boolean): Int {
if (!flag) fail("Flag must be true")
// ... compute result
return 42
}
You’ll occasionally see Nothing in more advanced APIs (like error() or TODO()), and understanding it helps interpret some of the stranger type error messages.
Example 8: Lambda type mismatch – function type vs value type
Lambdas are another common source of examples of type errors in Kotlin.
fun <T> applyTwice(value: T, f: (T) -> T): T = f(f(value))
fun main() {
val x: Int = 10
// Type mismatch: inferred type is Int but (Int) -> Int was expected
applyTwice(x, x + 1)
}
The second argument should be a function from Int to Int, but x + 1 is just an Int value.
Fix: pass a lambda or function reference instead.
applyTwice(x) { it + 1 }
// or
fun increment(i: Int): Int = i + 1
applyTwice(x, ::increment)
Any time you see a type error where the compiler expected something like (A) -> B, check whether you accidentally passed a value instead of a function.
2024–2025 trends: why Kotlin’s type errors matter more now
Kotlin keeps tightening its type system as the language matures. Recent compiler versions (including K2, the new frontend) are better at:
- Detecting incorrect nullability assumptions
- Inferring generic types
- Reporting clearer error messages
With Kotlin now a first‑class language for Android, server‑side development, and even data science, understanding these examples of type errors in Kotlin: examples and fixes is a real productivity boost. You’ll spend less time fighting the compiler and more time shipping code that doesn’t crash.
If you want to go deeper on static typing benefits in general, Harvard’s CS courses have accessible material on type systems and safety guarantees: https://cs50.harvard.edu/
FAQ: common questions about examples of type errors in Kotlin
What are the most common examples of type errors in Kotlin for beginners?
The most common examples include:
- Assigning
String?toStringwithout handling null - Returning
Intfrom a function declared to returnLong - Passing
MutableList<String>whereMutableList<Any>is expected - Forgetting that a lambda parameter expects a function type, not a value
If you’re new, pay extra attention to nullability and generics; most early type mismatch issues show up there.
Can I just disable strict type checks in Kotlin?
No. Kotlin is statically typed by design. You can loosen some checks with casts (as Any, as?) or by using platform types from Java, but that just moves the problem to runtime. The whole point of learning from these real examples of type errors in Kotlin is to fix the underlying type problems, not silence the compiler.
Is there a simple example of turning a runtime crash into a compile‑time type error?
A classic example is handling nulls coming from Java APIs. In Java, you might see a surprise NullPointerException when a method returns null. In Kotlin, if you declare the type as String? and then try to use it as String without a null check, the compiler gives you a type error instead of letting the crash reach production.
How can I get better at reading Kotlin type error messages?
Treat the message as a diff between expected and found types. Read it left to right:
- “Required:
List<String>” - “Found:
List<Any>”
Then ask: where did the expected type come from (function signature, variable declaration), and where did the found type come from (expression, literal, generic inference)? Once you train that habit on a few examples of type errors in Kotlin, your debugging speed goes way up.
Where can I learn more about Kotlin’s type system?
The official Kotlin documentation is the best starting point:
- Kotlin types overview: https://kotlinlang.org/docs/basic-types.html
- Null safety: https://kotlinlang.org/docs/null-safety.html
- Generics and variance: https://kotlinlang.org/docs/generics.html
While not Kotlin‑specific, general software safety resources from organizations like NIST (https://samate.nist.gov/) and university CS departments (for example, Harvard’s CS courses at https://cs50.harvard.edu/) help build the mental model behind why strong type systems catch bugs early.
By working through these real examples of type errors in Kotlin: examples and fixes, you’ll start to recognize patterns in the compiler’s complaints. Once you see those patterns, type errors stop feeling like random roadblocks and start acting like a very opinionated, very fast code reviewer that keeps your app from shipping with avoidable bugs.