Hilt Build Error on Kotlin 2.3.0: Provided Metadata instance has version 2.3.0 — Causes and Fixes Explained


error: [Hilt] Provided Metadata instance has version 2.3.0, while maximum supported version is 2.2.0.

This article explains the background of this error and introduces a new solution available since Dagger 2.57.

 

🤔 🧑🏻‍💻 1. Cause of the Error

This error occurs because kotlin-metadata-jvm, a library used internally by Dagger/Hilt, cannot understand the newer Kotlin metadata format (version 2.3.0).

Shading (Inshading) explained:

  • Shading means that a dependency is relocated and bundled inside another library’s JAR.
  • In earlier Dagger versions, kotlin-metadata-jvm was shaded (hidden) inside Dagger itself.
  • As a result, developers could not override or update it, even if Kotlin introduced a new metadata version.
  • This tightly coupled Dagger’s compatibility to a specific Kotlin version and forced users to wait for a Dagger release.

 

🤔 🧑🏻‍💻 2. What Changed in Dagger 2.57

Starting from Dagger 2.57, kotlin-metadata-jvm is unshaded (no longer hidden).

This means:

  • The dependency is now resolved normally via Gradle
  • Developers can explicitly specify a newer version without waiting for a Dagger update

This architectural change significantly improves Kotlin version agility.

 

🤔 🧑🏻‍💻 3. Solution: Explicitly Declare the Dependency

If you are using Kapt

Kapt runs through the Java compiler and is more sensitive to metadata incompatibility.


dependencies {
    // Add the latest metadata library to kapt
    kapt("org.jetbrains.kotlin:kotlin-metadata-jvm:2.3.0-Beta1")
}

If you are using KSP

KSP is directly integrated with the Kotlin compiler, so this error is less likely.

If needed, you can still specify it explicitly.


dependencies {
    // Add to ksp configuration
    ksp("org.jetbrains.kotlin:kotlin-metadata-jvm:2.3.0-Beta1")
}

Recommended: Force the version globally

If multiple modules are affected, this is the most reliable approach.


configurations.all {
    resolutionStrategy {
        force "org.jetbrains.kotlin:kotlin-metadata-jvm:2.3.0-Beta1"
    }
}

 

🤔 🧑🏻‍💻 4. Summary

  • If you are using Dagger 2.57 or later, you do not need to wait for a new Dagger release.
  • When the error appears, explicitly add the latest kotlin-metadata-jvm to your kapt or ksp configuration.
  • In general, migrating to KSP is recommended due to better compatibility and performance.
  • Developers who want to adopt the latest Kotlin features early should definitely apply this setup.

👉 Upgrade kotlin-metadata-jvm to support Kotlin 2.3.0 · Issue #5001 · google/dagger


【Android】How to handle UI interactions with event processing

 

🧑🏻‍💻 Summary

  • UI Only
    • Good for very small, UI-contained logic.
    • No state preservation; everything resets on recomposition.
    • Simple one-time actions still work fine.
    • Weak testability and scalability.
    • Poor separation of concerns without ViewModel.
  • StateFlow
    • Designed specifically for holding and exposing UI state.
    • Always provides the latest value after recomposition.
    • Not suitable for one-time events because old values persist.
    • Ideal for continuously changing UI states.
    • Assumes at least one active collector.
    • Ensures consistent UI across configuration changes.
  • SharedFlow
    • Optimal for one-time UI events (Toast, Snackbar, Navigation).
    • replay = 0 + extraBufferCapacity = 1 helps prevent event loss.
    • Supports multiple collectors safely.
    • Not intended for state storage.
    • Standard modern pattern for UI event handling.
    • Robust against lifecycle situations where collectors appear/disappear.
  • Channel
    • Best suited for FIFO, strictly ordered event delivery.
    • Should only have one collector (shared consumption is unsafe).
    • Ensures one-by-one processing with guaranteed order.
    • Less compatible with UI lifecycle due to collector timing issues.
    • Typically replaced by SharedFlow in UI-layer logic.
    • More appropriate for internal sequential pipelines rather than UI events.

👉 【Android】UIイベント整理術:StateFlow・SharedFlow・Channelの使い分け完全ガイド
👉 Why Kotlin Channels Are the Natural Solution for Preventing "Double Execution" of Events


Kotlin StateFlow: value vs. update – Which One Should You Use?

When updating a MutableStateFlow, you have two options. Here is how to decide instantly.

 

🧑🏻‍💻 The Golden Rule

  • Use update { } if the new value depends on the current value (e.g., incrementing a counter, toggling a boolean).
  • Use value = if you are completely overwriting the state (e.g., setting a loading state, resetting data).

 

🧑🏻‍💻 Why does it matter?

Direct assignment (value = ...) is not thread-safe for "read-modify-write" operations.

If two coroutines try to update the state simultaneously using .value = .value + 1, you risk a Race Condition where one update is lost.

The update function is atomic. It uses a Compare-And-Set mechanism to ensure that updates happen sequentially and safely, even across multiple threads.

 

🧑🏻‍💻 Code Comparison

❌ Risky (Race Condition prone)


// If called concurrently, updates might be lost
_uiState.value = _uiState.value.copy(count = _uiState.value.count + 1)

✅ Safe (Thread-safe)


// Guarantees consistency
_uiState.update { it.copy(count = it.count + 1) }

✅ Safe (Overwrite)


// No race condition risk because we ignore the previous state
_uiState.value = UiState.Loading

 

🧑🏻‍💻 Summary

When in doubt, use update. It is safer by default and prevents subtle concurrency bugs.