java.lang.NullPointerException: Parameter specified as non-null is null
kotlin.KotlinNullPointerException
Kotlin is designed to eliminate NullPointerExceptions, but you can still get them. When you do, itβs almost always from one of a few specific sources: Java interop, the !! operator, lateinit variables, or incorrect assumptions about platform types.
Why Kotlin still has NPEs
Kotlinβs null safety system works at compile time. The compiler tracks which variables can be null (String?) and which canβt (String), and forces you to handle the null case. But this system has gaps:
- Java interop β Java has no null safety. When Kotlin calls Java code, return types become βplatform typesβ with unknown nullability.
- The
!!operator β Explicitly tells the compiler βI know this isnβt null, trust me.β If youβre wrong, NPE. lateinitvariables β Bypass initialization checks. If accessed before assignment, NPE.- Reflection and serialization β Libraries like Gson can create objects without calling constructors, leaving non-null properties as null.
- Coroutine race conditions β Shared mutable state accessed from multiple coroutines without synchronization.
Fix 1: Replace !! with safe alternatives
The !! operator is the #1 cause of Kotlin NPEs. Every !! is a potential crash site.
// β Crashes if user is null
val name = user!!.name
val email = user!!.email
// β
Safe call β returns null if user is null
val name = user?.name
// β
Elvis operator β provide a default
val name = user?.name ?: "Unknown"
// β
Safe call chain
val city = user?.address?.city ?: "No city"
// β
let block for multiple operations on a nullable value
user?.let { u ->
println(u.name)
sendEmail(u.email)
updateLastLogin(u.id)
}
When !! is acceptable: Only when you have a logical guarantee that the value canβt be null at that point, AND a null would indicate a programming error that should crash. For example, after a null check in the same scope:
val user = findUser(id)
requireNotNull(user) { "User $id must exist at this point" }
// user is now smart-cast to non-null
println(user.name) // Safe, no !! needed
Fix 2: Handle Java interop correctly
When calling Java methods, Kotlin doesnβt know if the return value can be null. The type appears as a βplatform typeβ (shown as String! in IDE hints).
// Java method: public String getName() { ... }
// Kotlin sees this as String! (platform type β could be null)
// β Treating it as non-null β crashes if Java returns null
val name: String = javaObject.getName() // NPE if null!
// β
Treat as nullable
val name: String? = javaObject.getName()
val safeName = name ?: "default"
// β
Or use safe call immediately
val length = javaObject.getName()?.length ?: 0
For your own Java code: Add @Nullable and @NonNull annotations (from org.jetbrains.annotations or javax.annotation). Kotlin respects these and will enforce null safety at the boundary:
// Java
@NonNull
public String getName() { return name; }
@Nullable
public String getMiddleName() { return middleName; }
Fix 3: Initialize lateinit properly
lateinit tells the compiler βIβll initialize this later, before I use it.β If you access it before initialization, you get UninitializedPropertyAccessException (a subclass of NPE behavior).
class MyFragment : Fragment() {
lateinit var adapter: RecyclerAdapter
// β Accessing before onViewCreated assigns it
override fun onCreate(savedInstanceState: Bundle?) {
adapter.submitList(items) // Crash!
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
adapter = RecyclerAdapter() // Initialized here
adapter.submitList(items) // Safe
}
}
Check before access:
if (::adapter.isInitialized) {
adapter.update()
} else {
Log.w("MyFragment", "Adapter not yet initialized")
}
Alternative β use lazy instead of lateinit:
// Initialized on first access, never null
val adapter by lazy { RecyclerAdapter() }
Use lazy when the value can be computed from available data. Use lateinit when it depends on a lifecycle callback (like Androidβs onViewCreated).
Fix 4: Gson/Moshi serialization creating null non-null fields
JSON serialization libraries can bypass Kotlinβs null safety by creating objects via reflection without calling constructors.
// β Gson can set non-null fields to null
data class User(
val name: String, // Kotlin says non-null
val email: String // But Gson can make it null!
)
val user = Gson().fromJson("{}", User::class.java)
println(user.name) // NPE! name is null despite being String (not String?)
Fixes:
// β
Option 1: Use default values
data class User(
val name: String = "",
val email: String = ""
)
// β
Option 2: Use Moshi with kotlin-reflect (respects Kotlin nullability)
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
// β
Option 3: Use kotlinx.serialization (fully Kotlin-native)
@Serializable
data class User(
val name: String,
val email: String
)
Fix 5: Coroutine shared state
Mutable state accessed from multiple coroutines without synchronization can lead to NPEs when one coroutine nullifies a value another is reading.
// β Race condition
var currentUser: User? = null
launch { currentUser = fetchUser() }
launch {
delay(10)
println(currentUser!!.name) // Might be null!
}
// β
Use StateFlow or Mutex
private val _user = MutableStateFlow<User?>(null)
val user: StateFlow<User?> = _user
launch { _user.value = fetchUser() }
launch {
user.filterNotNull().first().let { u ->
println(u.name)
}
}
Fix 6: Android-specific NPE sources
Common Android NPE patterns:
// β View binding after fragment view is destroyed
override fun onDestroyView() {
super.onDestroyView()
// binding is now null, but you might still reference it in a callback
}
// β
Use viewLifecycleOwner for observers
viewLifecycleOwner.lifecycleScope.launch {
viewModel.data.collect { binding.textView.text = it }
}
// β getActivity() returns null after detach
val ctx = requireContext() // Crashes if fragment is detached
// β
Check first
context?.let { ctx ->
Toast.makeText(ctx, "Done", Toast.LENGTH_SHORT).show()
}
Prevention strategies
-
Ban
!!via lint β Use detektβsUnsafeCallOnNullableTyperule:# detekt.yml potential-bugs: UnsafeCallOnNullableType: active: true -
Enable strict mode for Java interop β Treat all platform types as nullable by default in your team conventions.
-
Use
requireNotNull()with messages instead of!!:val user = requireNotNull(findUser(id)) { "User $id not found in database" }This gives you a meaningful error message instead of a bare NPE.
-
Prefer
valovervarβ Immutable values canβt become null after initialization. -
Use sealed classes instead of nullable types for states:
sealed class Result<T> { data class Success<T>(val data: T) : Result<T>() data class Error<T>(val message: String) : Result<T>() class Loading<T> : Result<T>() }
FAQ
Why does Kotlin show βParameter specified as non-null is nullβ?
This happens when Java code passes null to a Kotlin function that declares a non-null parameter. Kotlin adds a null check at the function entry point and throws this specific NPE. Add @Nullable to the Java caller or make the Kotlin parameter nullable.
Is ?.let { } better than if (x != null) { }?
For single operations, if is clearer. For chaining or when you need the non-null value in a lambda scope, let is cleaner. They compile to the same bytecode.
Can I get NPE from a val property?
Yes, if itβs backed by a custom getter that calls Java code, or if itβs a delegated property whose delegate returns null for a non-null type.
How do I find all !! usages in my project?
In IntelliJ/Android Studio: Edit β Find β Find in Files β search for !!. Or use detekt to report them as warnings in CI.