Introduction

Moshi enforces strict JSON parsing: when a Kotlin data class has a non-nullable property without a default value, Moshi requires that property to be present in the JSON. If the API omits the field, Moshi throws a JsonDataException with a clear message about which property is missing. While this is safer than silently inserting null, it causes crashes when APIs evolve or return incomplete data.

Symptoms

  • com.squareup.moshi.JsonDataException: Required property 'email' missing at $
  • Deserialization fails entirely when one field is absent
  • Works for some API responses but not others
  • Error message identifies the exact missing property
  • Crash occurs after API adds optional fields

Example error: `` com.squareup.moshi.JsonDataException: Required property 'email' missing at $.path[0] in response.json at com.squareup.moshi.internal.Util.missingProperty(Util.java:623) at com.example.UserJsonAdapter.fromJson(UserJsonAdapter.kt:32)

Common Causes

  • API does not return a field that the Kotlin data class requires
  • New API version restructures the response
  • Field renamed in API but Kotlin model not updated
  • Conditional fields: API only returns certain fields in certain states
  • Using Kotlin data class with non-nullable properties for API data

Step-by-Step Fix

  1. 1.Make the property nullable with a default:
  2. 2.```kotlin
  3. 3.// Before: crashes if email is missing
  4. 4.data class User(
  5. 5.val id: String,
  6. 6.val name: String,
  7. 7.val email: String // Required - crash if missing
  8. 8.)

// After: gracefully handles missing email data class User( val id: String, val name: String, val email: String? = null // Optional with default ) ```

  1. 1.Use Moshi Kotlin reflection with default values:
  2. 2.```kotlin
  3. 3.// Moshi's Kotlin integration respects default values
  4. 4.data class User(
  5. 5.val id: String,
  6. 6.val name: String,
  7. 7.val email: String = "unknown@example.com",
  8. 8.val role: String = "user"
  9. 9.)

val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) // Required for default value support .build()

val adapter = moshi.adapter(User::class.java)

// JSON missing email and role - uses defaults val user = adapter.fromJson("""{"id":"1","name":"John"}""") // user = User(id=1, name=John, email=unknown@example.com, role=user) ```

  1. 1.Handle nested object that might be missing:
  2. 2.```kotlin
  3. 3.data class ApiResponse(
  4. 4.val data: Data? = null,
  5. 5.val error: ErrorInfo? = null
  6. 6.)

data class Data( val user: User? = null, val settings: Settings = Settings() // Default empty settings )

data class Settings( val notifications: Boolean = true, val theme: String = "system" ) ```

  1. 1.Create a custom adapter for complex defaults:
  2. 2.```kotlin
  3. 3.class UserAdapter {
  4. 4.@FromJson
  5. 5.fun fromJson(reader: JsonReader): User {
  6. 6.var id: String? = null
  7. 7.var name: String? = null
  8. 8.var email: String? = null

reader.beginObject() while (reader.hasNext()) { when (reader.nextName()) { "id" -> id = reader.nextString() "name" -> name = reader.nextString() "email" -> email = reader.nextString() else -> reader.skipValue() } } reader.endObject()

return User( id = id ?: throw JsonDataException("id is required"), name = name ?: "Anonymous", email = email ) } } ```

Prevention

  • Always add KotlinJsonAdapterFactory to Moshi for default value support
  • Use nullable types with defaults for all API response properties
  • Use val with default values instead of lateinit for JSON models
  • Add integration tests that parse actual API responses
  • Use Moshi's @Json(name = "alternative_name") for field renaming
  • Monitor JsonDataException rates to catch API changes early