skip to content

A teammate upgraded Kotlin but the build now fails serializing a class, or @Serializable seems ignored. How do you diagnose plugin/version issues with kotlinx.serialization?

level: middleimportance: should knowfreq 45%

answer

  1. Plugin version == Kotlin version; runtime version independent
  2. Plugin must be applied in THIS module
  3. 'Serializer has not been found' -> missing annotation/plugin or third-party type
  4. Use version catalog/BOM to keep versions in lockstep
  5. Third-party types -> custom KSerializer via with= or @Contextual

basics

~10 s

Check that the serialization plugin version matches the new Kotlin version, that the plugin is actually applied, and that the runtime library is present. Mismatches or a missing plugin are the usual cause.

solid answer

~50 s

First confirm the **plugin is applied** in the right module's `plugins {}` block (not just in the root) — `@Serializable` does nothing without it, and a missing serializer surfaces as 'Serializer for class X has not been found' or a compile error at the encode call. Second, confirm the **plugin version tracks the Kotlin version**: after a Kotlin bump, the `kotlin("plugin.serialization")` version must move to the same value (best done via the Kotlin BOM / version catalog or by sharing one Kotlin version variable). A stale plugin against a new compiler can fail to load or emit incompatible code. Third, ensure the **runtime library** (`kotlinx-serialization-json`) is on the classpath; its version is independent but must be present. For third-party types you don't own, you need a custom `KSerializer` or `@Serializable(with=...)` / contextual serializer — the plugin can't generate one for a class it can't annotate.

go deeper

for a junior

Can check that the plugin and dependency exist and that the class is annotated.

for a middle

Distinguishes the two version axes and diagnoses the common 'serializer not found' error correctly.

for a senior

Uses a version catalog/BOM to lock versions, and handles third-party types via custom/contextual serializers.

for a principal

Owns the upgrade strategy across many modules — centralized version governance, custom serializer registries, and rollout/testing of Kotlin bumps.

## The two-axis version model There are **two independent versions** in play and conflating them causes most issues: - **Compiler plugin** `kotlin("plugin.serialization")` → **must equal the Kotlin version** (e.g. both 2.1.0). It's tied to compiler internals. - **Runtime library** `kotlinx-serialization-json:1.x` → **independent**, backward compatible across a range of Kotlin versions. ## Diagnostic checklist 1. **Is the plugin applied in this module?** In multi-module builds, applying it only in the root or a sibling means `@Serializable` is inert here. Symptom: `SerializationException: Serializer for class 'Foo' has not been found` or unresolved `serializer()`. 2. **Does the plugin version match Kotlin?** After a Kotlin upgrade, bump the plugin to the same version. Centralize with a version catalog: ```toml [versions] kotlin = "2.1.0" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } ``` 3. **Is a format runtime present?** You need `kotlinx-serialization-json` (or cbor/protobuf). `-core` alone gives you interfaces but no Json format. 4. **Is the type one you can annotate?** You cannot `@Serializable` a third-party class. Use a custom serializer and apply it via `@Serializable(with = MySerializer::class)` on the property, or register it **contextually** in a `SerializersModule` and use `@Contextual`. 5. **Enums/sealed/generics**: enums work out of the box; sealed classes get a sealed serializer; generic classes generate a parameterized serializer — but each *concrete* type still needs `@Serializable`. ## Reading the error - 'has not been found for type' → missing `@Serializable` or missing plugin in that module, or a third-party type needing a custom/contextual serializer. - Compiler crashes/incompatible metadata after upgrade → plugin/Kotlin version skew. ## Fix patterns ```kotlin // Third-party type you can't annotate: object InstantSerializer : KSerializer<Instant> { /* ... */ } @Serializable data class Event(@Serializable(with = InstantSerializer::class) val at: Instant) ```

  • Why can't the plugin generate a serializer for a java.time.Instant field?
    The plugin only generates for classes you annotate with @Serializable, and you can't annotate a third-party/JDK class. You supply a custom KSerializer and attach it via @Serializable(with=...) on the property or register it contextually in a SerializersModule with @Contextual.
  • How do you keep the plugin and Kotlin versions from drifting apart?
    Drive both from a single source — a Gradle version catalog `version.ref` or the Kotlin Gradle plugin BOM — so a Kotlin bump moves the serialization plugin automatically.

saying these in an interview costs you the question

  • Bumping the runtime library to 'fix' a Kotlin-version mismatch (wrong axis)
  • Not realizing the plugin must be applied per-module
  • Trying to @Serializable a third-party class directly
  • Adding only -core and expecting Json to work
  • Assuming the runtime version must equal the Kotlin version

context