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?
answer
- Plugin version == Kotlin version; runtime version independent
- Plugin must be applied in THIS module
- 'Serializer has not been found' -> missing annotation/plugin or third-party type
- Use version catalog/BOM to keep versions in lockstep
- Third-party types -> custom KSerializer via with= or @Contextual
basics
~10 sCheck 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 sFirst 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
Can check that the plugin and dependency exist and that the class is annotated.
Distinguishes the two version axes and diagnoses the common 'serializer not found' error correctly.
Uses a version catalog/BOM to lock versions, and handles third-party types via custom/contextual serializers.
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