Releases: InsertKoinIO/koin-compiler-plugin
Release list
1.1.0
A compile-safety architecture release: per-module validation is removed entirely in favor
of a single, authoritative full-graph check at each Koin entry point. This closes a real,
measured false-positive class, at the cost of leaf modules with no entry point of their own now
getting no compile-time safety diagnostics until something assembles a real graph around them.
Also ships incremental-compilation freshness hardening, allWarningsAsErrors compatibility, and a
collision-safe hint-file-naming scheme.
⚠️ Behavior change — per-module validation removed, full-graph validation only (#32, #51)
Why. A module validated in isolation cannot know how it will be wired into a larger app. This
stopped being theoretical: :core:notifications in a real playground app genuinely false-positived
on a dependency (PeerService) that a peer module provides — with no Gradle edge between the two,
the two are only unified downstream at the app's entry point. Per-module validation — checking a
module against its own definitions, its includes = [...], and its @Configuration siblings —
cannot see that far, and reported a hard KOIN-D001 for a dependency that resolves correctly once
the real app assembles both modules together. Rather than keep tuning the per-module oracle around
each new false-positive shape, per-module validation (and its "defer iff a provider exists
somewhere" oracle) is deleted outright. Full-graph validation — the check that runs at
startKoin/koinApplication/@KoinApplication — is now the sole compile-safety verifier.
What this means for you:
- Rooted compiles (an app module with a real
startKoin/koinApplication/@KoinApplication):
more accurate. Genuine cross-module false positives like the peer-provider case above disappear;
KOIN-D001now always shows the real, assembled graph. - Leaf/library modules with no Koin entry point in their own compilation:
KOIN-D001
(missing dependency),KOIN-D004(circular dependency),KOIN-D005/KOIN-D006(parametersOf
shape mismatches resolved via the graph), andKOIN-P001(missing@PropertyValue) are now
silent in that compilation — not because the module is safe, but because compile-time cannot
know how it will be assembled downstream. The graph is still checked, correctly, at the real
entry point once one exists in the compilation. This is disclosed via a default-visible
(INFO-severity) message rather than failing silently; seelogSeveritybelow to control its
visibility. KOIN-W002(the old "deferred, no provider hint found anywhere" warning) is deleted — there
is no more deferral machinery to warn about.- Circular-dependency detection (
KOIN-D004) going silent for a leaf module is intentional, not a
regression: detecting a cycle requires seeing the whole graph, and a same-module-only check was
never a complete cycle detector even under the old per-module validation (it only ever saw
local/sibling visibility).
Full account, including the design docs this reverses: docs/COMPILE_SAFETY_A3_PLAN.md
(superseded-banner) and docs/COMPILE_TIME_SAFETY.md.
🐛 Fixes
Orphaned @Module classes were silently treated as reachable (found during this release's own verification)
A plain @Module @ComponentScan(...) class with no @Configuration and not referenced by anyone's
includes = [...] was silently treated as part of the graph anyway, as long as the entry point used
a bare/default-labeled @KoinApplication/startKoin — the overwhelmingly common case. Its
@ComponentScan-discovered definitions (including cross-module ones) were folded into the resolved
graph and validated as satisfied, when the actual generated module tree never wired them in at all:
build green, runtime crash. Root cause: the entry-point module-discovery step accidentally called a
label-reader meant for the entry-point class's @KoinApplication(configurations=[...]) argument
against module classes, which never carry that annotation — so it always hit that reader's
"annotation absent" fallback (["default"]), making any @Module class match. This bug predates
1.1.0 (traced to 1.0.0-GA1) but was masked by the old per-module validation, which used to
validate each such module in isolation too; removing it made this bug load-bearing. Fixed, with a
regression test
(entry_orphan_module_not_reachable_d001) proving an orphaned module without @Configuration or an
includes edge is now correctly excluded from the graph.
KOIN-D001 now names the real culprit module and source location
Missing-dependency errors now carry file:line for the failing definition and the actual owning
module's name (previously degraded to a generic app/root label once every KOIN-D001 funnels
through the one remaining full-graph check). Also fixed: attribution for FunctionDef-shaped definitions
used a bare simple name, which could collide across same-named modules in different packages — now
uses the fully-qualified name.
KOIN-D001 deduplication across multiple entry points
A module reachable from more than one startKoin/koinApplication/@KoinApplication in the same
compilation (common in test-apps: ~9 entry points is typical) previously re-validated and re-emitted
the same missing-dependency error once per entry point. Now deduplicated by (definition, missing
requirement), so a shared module with one real problem reports it exactly once.
D005/D006 (parametersOf shape checks) no longer require a Koin entry point
The parametersOf(...) argument-count/presence check is graph-independent — the expected slots come
from the target's own constructor, not from an assembled graph — so it now runs unconditionally
instead of being skipped whenever no entry point is present in the compilation, matching its actual
data dependency. KOIN-D002 (call-site resolution) correctly keeps requiring an assembled graph
and stays silent without one — the two diagnostics no longer share a gate they don't share a
dependency on.
Cross-module qualifier and typed-scope resolution verified under the new sole-verifier design
New regression coverage confirms full-graph validation matches @Named qualifiers and typed
@Scope(X::class) keys correctly across Gradle module boundaries, not just "some provider of this
type exists somewhere" — this matters more now that there's no per-module fallback to catch a wrong
match.
Known pre-existing limitation, found while writing this coverage (not new, not fixed this
release): BindingRegistry.findProvider's scope-visibility check only matches a typed
@Scope(X::class); a named @Scope(name = "...") provider has no scopeClass and is treated
as visible everywhere regardless of name.
🔒 Incremental-compilation freshness
Removing per-module leaf-local checking made full-graph validation's own freshness across
incremental (IC) rebuilds load-bearing in a way it wasn't before — these changes close that gap:
strictSafetyis now mandatory once an aggregator is auto-detected, not opt-in. Previously,
an explicitstrictSafety = falsesilently won over the plugin's ownstartKoin/
koinApplication/@KoinApplicationdetection, letting an aggregator'scompileKotlinstay
cacheable/up-to-date even when the DI graph changed underneath it (lambda-body DSL edits and
new@ComponentScan-covered files don't register as ABI changes IC can see).strictSafety = true
still works everywhere; the new escape hatch for a genuine detector misfire (the marker appears
only in a comment/string, not a real entry point) isstrictSafetyForceOff = true— a separate,
explicit acknowledgement from a plainfalse.- Extended IC tracker linking:
KoinDSLTransformer's 5 DSL definition call sites now register
withExpectActualTracker(alongside the existingLookupTrackercalls), matching the pairing
KoinAnnotationProcessor/KoinStartTransformeralready had — closes another source of stale
incremental state around DSL hint files. - A theorized
@ComponentScannew-file freshness gap did not reproduce: adding a new
@Singleton/@Factoryclass to a scanned package is itself a source-set input change, which
Gradle already invalidates the owning module'scompileKotlintask for, independent of anything
Koin-specific — verified live on a real playground app. No plugin-side fix was needed here. - Known limitation, unchanged by this release: a module going completely empty (its last
includes()or its last local definition removed, with nothing replacing it) is not detected
incrementally without a full clean +--no-build-cache. This is a K2-internals residual (a
keep-alive hint's signature not being re-resolved within one IC session), not a missing source
edge — seeplayground-apps/README.md's "Known limitation" note.
🔇 allWarningsAsErrors / -Werror compatibility (#73)
Informational plugin output (userLogs/debugLogs messages, the @Monitor-tracing-enabled
summary) was emitted at WARNING severity unconditionally, which fails a build compiled with
allWarningsAsErrors even though none of it is a real diagnostic.
- New
logSeverityoption ("warning"default, or"info") covers all of the above. - New, separate
versionCheckSeverityoption covers only the Kotlin-version-compatibility
warning ("you're on an unverified Kotlin version") — kept independent because muting informational
noise shouldn't also silence a real compiler-compatibility risk; set it to"info"only after
assessing that risk yourself. - Real diagnostics (
KOIN-Dxxx/KOIN-Wxxx/etc.) are unaffected by either setting — they always
report at their own severity.
koinCompiler {
logSeverity = "info" // downgrade informational output, default "warning"
versionCheckSeverity = "info" // downgrade the version-compatibility check, default "warning"
}🧷 Hint-file collision sa...
1.0.2
A correctness-focused maintenance release: it removes several false compile-safety errors in multi-module projects, fixes duplicate-hint KLIB failures on iOS/Native/WASM-JS, and hardens per-compilation state under parallel Gradle daemons.
🔑 Highlights
- No more false "missing dependency" errors across modules — a
@Modulewhose dependency is provided by a sibling module (assembled only at@KoinApplication/startKoin) no longer fails withKOIN-D001. It defers to the entry-point graph, and surfaces a newKOIN-W002warning only when no complete closure is visible in the compilation (#51). - iOS / Native / WASM-JS build reliability — cross-module
@ComponentScanand cross-module top-level@Singlefunctions no longer emit duplicate hint declarations that broke KLIB serialization (#62). - Fewer false compile-safety errors on DSL code — typed DSL definitions with custom lambdas, indirect
parametersOfhelpers, and outer DSL qualifiers are now understood (#36, #49, #61, #41). - Compose entry point validated —
KoinApplication(configuration = koinConfiguration { … })now runs full-graph safety (#38).
🐛 Fixes
False KOIN-D001 for cross-module (sibling) dependencies — #51 (KTZ-4256)
In a layered multi-module build, a @Module is compiled without visibility of the sibling modules a downstream @KoinApplication(modules = […]) assembles alongside it. Per-module (A2) validation therefore reported a provider that lives in a sibling as a hard KOIN-D001 missing dependency. Validation now defers an unresolved binding when a provider hint for the type exists elsewhere on the build graph, settling it authoritatively at the entry-point closure (A3) or at runtime checkModules(). When no complete closure is present in the compilation (e.g. a leaf library module), it emits the new KOIN-W002 warning instead of an error.
Scope: this narrows the false positive to the common shape (provider is a compile dependency, or compiled alongside the consumer). A genuine missing dependency with no provider hint anywhere is still a hard
KOIN-D001. A provider that lives in a non-dependency peer module (type declared in a shared module) is not yet distinguishable at the leaf and may still report — full A2 relaxation is planned for 1.1.
Duplicate hint declarations broke iOS / Native / WASM-JS — #62 (KTZ-4365)
A cross-module @ComponentScan covering a dependency module's package, and cross-module top-level @Single functions, could register the same definition more than once — emitting duplicate componentscan_* / definition_function_* hint declarations. The JVM/DEX toolchain tolerated it (a D8 "multiple definitions" warning); KLIB serialization (iOS/Native/WASM-JS) rejected it with a hard SignatureClashDetector error. Definitions are now de-duplicated by class identity (and by type+qualifier for functions), so each is emitted exactly once per target.
False KOIN-D001 for typed DSL definitions with non-create lambdas — #36, #49
single<T> { existingInstance }, single<T> { provideX() }, viewModel { VM() } and similar shapes are now recognized as providing T, so compile-safety no longer reports T as a missing definition. The user's lambda is left untouched.
False KOIN-D006 for indirect parametersOf helpers — #61
A call site passing an opaque params lambda (e.g. { buildParams() }) no longer triggers KOIN-D006 ("forgot parametersOf"). The diagnostic now fires only when no params lambda is present at all.
Qualifier lost on DSL create definitions — #41
An outer DSL qualifier (single<T>(named("x")) { create(::T) }) is now propagated into the compile-safety hints, so qualified cross-module definitions resolve correctly instead of producing spurious mismatches.
Compose koinConfiguration { } entry point not validated — #38
KoinApplication(configuration = koinConfiguration { modules(…) }) is now recognized as a Koin entry point, enabling full-graph (A3) compile-safety for Compose apps. The koinConfiguration call is only marked as an entry point — it is not rewritten, so runtime behavior is unchanged.
Flaky / order-dependent behavior under parallel Gradle daemons — (KTZ-4414)
Plugin config flags and the @PropertyValue registry were held in process-global mutable state shared across every compilation in a Gradle daemon. Parallel or interleaved compilations could read another build's flags or have a @PropertyValue default dropped. State is now held per-compilation (thread-local, rebound onto the IR phase), matching the existing per-compilation message-collector handling.
⚠️ Auto-binding excludes framework/marker supertypes — #43, #64
This changes generated code and can affect runtime resolution. Auto-detected bindings no longer include framework plumbing / marker supertypes: kotlin.Any, org.koin.core.component.KoinComponent, KoinScopeComponent, and androidx.lifecycle.ViewModel / AndroidViewModel. Previously a @KoinViewModel / @Single class implementing one of these could be auto-bound to the framework base type, letting get<ViewModel>() / get<KoinComponent>() resolve to an arbitrary component (silent wrong-instance resolution). A definition is now registered under its own type and its genuine domain interfaces only.
Explicit bindings are unaffected — @Single(binds = [ViewModel::class]) still binds exactly what you list. If you relied on auto-binding to one of the excluded supertypes, add it explicitly with binds = [...].
✅ Compatibility
| Kotlin 2.3.20 | Kotlin 2.4.0 | |
|---|---|---|
| JVM / Android | ✅ | ✅ |
| iOS / Native | ✅ | ✅ |
| WASM/JS — DSL | ✅ | ✅ |
| WASM/JS — annotations | ✅ |
- Koin: 4.2.0+
📦 Install
plugins {
id("io.insert-koin.compiler.plugin") version "1.0.2"
}Full changelog: 1.0.1...1.0.2
1.0.1
A maintenance release focused on Kotlin 2.4.0 support and Kotlin/Native + WASM/JS build reliability. One plugin artifact now spans Kotlin 2.3.20 → 2.4.x.
🔑 Highlights
- Kotlin 2.4.0 support — the plugin no longer crashes on Kotlin 2.4.0, and the same artifact also works on Kotlin 2.3.20.
- iOS / Native / WASM builds fixed — annotation definitions no longer break KLIB serialization.
@Single(createdAtStart = true)honored on definition functions — eager singletons are created atstartKoinagain.
🐛 Fixes
Kotlin version compatibility — #19, #42 (and koin#2431)
The plugin hard-crashed on the two most recent Kotlin versions:
- Kotlin 2.4.0 —
ClassCastExceptionduring FIR extension registration. - Kotlin 2.3.20 —
NoSuchMethodError(IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB).
1.0.1 introduces a Kotlin version-adapter layer: the core is compiled against the stable IR API, and a small per-version adapter (selected at compile time) absorbs the breaking compiler-internal differences. A single published jar supports Kotlin 2.3.20 and 2.4.0; an unrecognized newer Kotlin gets a warning and best-effort support, while versions below the floor get a clear, actionable error instead of an internal crash.
Duplicate injectedparams_* signatures broke iOS + WASM/JS — #44, #40
A type collected by more than one @ComponentScan module generated the @InjectedParam hint function twice with identical signatures. The JVM tolerated it; KLIB serialization (iOS/Native/WASM/JS) rejected it with "Different declarations with the same signatures". The hint is now emitted exactly once per target. (Regression vs 1.0.0-RC2.)
WASM/JS KLIB serialization — #40
The plugin's generated hint files lacked a resolvable source, failing the JS/WASM KLIB serializer ("No file found for source null"). Fixed — DSL-based projects now build on WASM/JS.
@Single(createdAtStart = true) silently dropped — koin#2425, koin#2415
createdAtStart = true on a @Single / @Singleton definition function inside a @Module was discarded in codegen, so eager singletons were never created at startKoin (no error or warning). Now propagated correctly. (The @Module(createdAtStart) and @Singleton class cases were already fixed in 1.0.0-RC3.13.)
⚠️ Known limitation — annotation-based WASM/JS on Kotlin 2.3.20
Annotation-based projects (@Module / @ComponentScan) targeting WASM/JS require Kotlin 2.4.0. On Kotlin 2.3.20 they hit an upstream Kotlin compiler bug — KT-82395 — in the JS/WASM KLIB metadata serializer, which the plugin cannot work around. Kotlin 2.4.0 resolves it. (iOS/Native and DSL-based WASM/JS are unaffected and work on both Kotlin versions.)
✅ Compatibility
| Kotlin 2.3.20 | Kotlin 2.4.0 | |
|---|---|---|
| JVM / Android | ✅ | ✅ |
| iOS / Native | ✅ | ✅ |
| WASM/JS — DSL | ✅ | ✅ |
| WASM/JS — annotations | ✅ |
📦 Install
plugins {
id("io.insert-koin.compiler.plugin") version "1.0.1"
}Full changelog: 1.0.0...1.0.1
1.0.0
Koin Compiler Plugin 1.0.0
The first stable release. RC1 shipped in April 2026; over the following weeks the safety pass, incremental-compilation handling, and K2.3.20 compatibility came together. The compiler-plugin path is now ready for production: full-graph compile-time safety, cross-module call-site validation, IC-aware safe-defaults, and K2.3.20 support.
Requires: Kotlin 2.3.20+ (K2) | Koin 4.2.1+
What's New Since 1.0.0-RC2
Compile-time safety expanded (A2/A3/A4)
- Circular dependency detection — cycles like
A → B → Aare caught during graph traversal in A2/A3, no longer waiting for runtime. - Call-site validation with dynamic parameters —
get<T> { parametersOf(...) }and ViewModel/inject sites withparametersOf {}flows are validated against the assembled graph. - Compose-wrapped lambdas — A4 traverses Compose lambdas, so
koinViewModel<T>()inside@Composablebuilders is validated like any other call site. - Qualifier-collision guard — A4 reports when two definitions resolve to the same
(raw class + qualifier)key, preventing silent last-wins overrides at runtime. KOIN-D007—@Factoryreturning a type that extends a suspendfun interfaceis now blocked at compile time (previously crashed Fir2Ir).- Bare
koinConfiguration/koinApplication/startKoinno longer bypass A3 — the full-graph pass runs even without an explicit<T>type parameter. - Cross-module DSL visibility under typed
startKoin<T>()— A4 now sees DSLmodule { … }definitions declared in dependency JARs when the aggregator uses the typed entry point.
Incremental compilation & build robustness
strictSafetyflag — auto-enabled on modules that containstartKoin,koinApplication, or@KoinApplication. Forces the full-graph safety pass to re-run on the aggregator each build, working around two K2 IC gaps: DSL changes insidemodule { }lambda bodies (not part of any declaration's ABI) and@ComponentScanpackage-scope discovery (no source-level edge). Library and feature modules stay fully incremental.- Module-disambiguated hint file names — stable anchors prevent cross-module
Hints.ktcollisions during multi-platform builds. - kapt / Hilt coexistence — defensive guard around
KtPsiSourceElement.psiprevents Fir2Ir crashes when the plugin runs alongside kapt or Hilt during migration.
Kotlin 2.3.20 compatibility
- Fir2Ir crash on K2.3.20 —
HINTS_PACKAGEis now claimed unconditionally, fixing a regression when building against the latest K2.
Annotation fixes
@Module(createdAtStart = true)— was silently ignored; now correctly forwards the flag to the generatedmodule { }.@Scope(name = "…")— previously produced no bean definition; the scope DSL is now generated as expected.@ScopeId(name = "…")— resolution behaviour locked in via regression coverage.
Diagnostics
- CTA banner ordering — error reports anchor on the last error in the chain and use a per-extension collector, so the actionable hint is always closest to the offending call site.
- Compiler error info — error frames now embed the diagnostic code and a single-line "how to fix" pointer.
Performance
- Memoized
startKoinmodule discovery — repeated scans during a single compilation reuse the resolved module set. - Indexed
@ComponentScanfiltering — package-scope discovery now uses an indexed lookup instead of repeated linear scans, noticeably faster on large multi-module projects.
Repo / docs
- Playground apps moved into the main repo (
playground-apps/app-dsl,playground-apps/app-annotations) — single clone, single build, no separate repo to keep in sync. - Documentation updates —
strictSafety, K2.3.20 minimum, circular-dep detection at compile time, KOIN-D007.
Behaviour changes to note when upgrading
strictSafety is on by default on aggregator modules. If your app contains startKoin, koinApplication, or @KoinApplication, that module's compileKotlin task will re-run the A3 safety pass on every build (library/feature modules unaffected). The cost is bounded, and it closes the IC gaps that previously let graph changes slip through cached builds. Set koinCompiler { strictSafety = false } to opt out.
Kotlin minimum bumped to 2.3.20 (was 2.3.0) — the Fir2Ir fix requires the newer compiler. If you're pinned to 2.3.0, stay on 1.0.0-RC2 until you can upgrade.
Closed issues since the project went public
Fixed
- #1 — FileAnalysisException during compilation (@FatalCatharsis)
- #2 — Unrecognized
@ScopeId(@limuyang2) - #3 — Use a stable Kotlin version (@adamglin0)
- #7 —
@Providedstill flagging missing dependencies (@kmbisset89) - #8 — Not generating a module (@kmbisset89)
- #11 — Incomplete IR type generation for
Scope.get()(@alex28sh) - #12 — Runtime stack overflow with Kotlin delegation pattern (@pupava)
- #14 — Generated
.module()extension not recognized by IntelliJ (@DenAbr) - #15 — Cannot fetch 0.6.1 version (@FSBlocks)
- #16 — Compiler crash on
@Factoryreturningfun interfaceextending suspend function type (@krzdabrowski) — now also blocked diagnostically via KOIN-D007 - #17 — Incompatibility with Arrow core (@gael-ft)
- #18 — Compile-time safety breaks Native builds with generic types (@alex-z0)
- #20 — Duplicate callsite hint classes in multi-module builds (@norbertsitko)
- #22 — Compiler plugin misses cross-module bindings from
@Single(binds = [...])provider function (@flaringapp) - #24 — IR crash on generic ViewModel base class on Kotlin/Native (iOS) (@hmy65)
- #29 — FileAnalysisException when migrating from Hilt (@theimpulson)
- #32 —
compileSafetymissing binding silently passes on incremental compilation (@j-bajon) — drove thestrictSafetydesign - #34 —
@Scope(name=…)+@Scoped(binds=[…])silently produce no bean definitions (@rduriancik) - #35 — Module doesn't contain package
org.koin.plugin.hints(@0xMatthewGroves) - #38 —
compileSafetychecks bypassed when usingKoinApplicationComposable in CMP (@rajdeepvaghela)
Won't fix / deferred
- #21 — IDE cannot resolve generated
module()extension (@JordanLongstaff) — documented as a known limitation pending IDE-side support - #33 — Feature request: wasmJs target support (@dmitry-stakhov) — tracked for a future release
Cross-repo fixes
- koin#2368 —
@ScopeId(name = "…")resolution behaviour - koin#2380 — typed
startKoin<T>+@KoinApplication(modules=[…])+ scanned@KoinViewModel - koin#2400 — nested DSL
includes(...)reachability - koin#2402 — explicit
@KoinApplication(modules = [...])overrides discovered@Configuration
Contributors
Project lead: @arnaudgiuliani (Arnaud Giuliani)
Code contributions (commit authors and merged PRs across all releases):
- @flaringapp — explicit bindings in
@Moduleprovider functions (PR #23) - @JellyBrick — IR generation OOM fix (PR #5), Gradle release signing fix (PR #4)
- @wjz2001 — cross-module
@ComponentScanhints without@Configuration([PR #25](https://github.com/InsertKoinIO/koin-compiler-plugin/pul...
1.0.0-RC2
Koin Compiler Plugin 1.0.0-RC2
Stability pass on top of RC1: fixes user-reported crashes on iOS/Native, multi-module dex-merge collisions, and a module load-order behaviour change that makes app-level overrides actually win.
Requires: Kotlin 2.3.x+ (K2) | Koin 4.2.1+
What's New Since 1.0.0-RC1
Cross-module visibility
@Module + @ComponentScanwithout@Configurationnow emits cross-module scan hints — previously scan-covered definitions were invisible to downstreamcompileSafetyunless the module was also@Configuration. See PR #25.@Single(binds = [...])on@Moduleprovider functions preserves binding metadata across module boundaries — consumers can now resolve interfaces bound via provider functions in a dependency JAR. See PR #23 / #22.
Fixes
- Module load order (koin#2402) — auto-discovered
@Configurationmodules now load first and explicit@KoinApplication(modules = [...])load last, so app-level overrides win over dependency defaults under Koin's last-wins semantics. - Duplicate call-site hint classes in multi-module Android builds (#20) — hint filenames are now prefixed with the compilation module identifier; no more
org.koin.plugin.hints.XxxCallsiteKtcollisions at dex merge. - Generic DSL types on iOS/Native (#18) — Kotlin/Native klib signature mangler no longer crashes on generic DSL types (
single<Navigator<Key>>()). Hint emission erases type arguments to match runtime Koin's erasure behaviour. - Arrow
Raise.bind()/ ktorresourceScope { bind() }crash (#17) —collectBindTypenow matches Koin'sbindby FqName, no longer intercepting unrelated librarybindfunctions. single<T> { create(::Impl) }with interface type parameter — the outer<T>is now registered as the provided type (previouslyImpl), sokoin.get<Interface>()compile-safety passes as expected.- Missing DSL-artifact error (RC2.3) —
@KoinViewModel/@KoinWorkerwithout their runtime artifact (koin-core-viewmodel/koin-android-workmanager) now fails the build with a clear message pointing at the missing dependency, instead of silently producing a broken definition. - Unit-returning top-level
@Singletonfunctions (RC2.2) — klib signature-clash fix; two qualified side-effect initializers no longer collide on iOS/JS/Wasm. - Custom qualifier annotations (RC2.1) — plain custom
@Qualifierannotations now produceTypeQualifier, matching runtimenamed<T>()/typeQualifier<T>()semantics.
Documentation
Extended docs on module load order, generic DSL types (with the named<T>() qualifier pattern used by koin-compose-navigation3), and troubleshooting the new missing-artifact compile error.
Behaviour change to note when upgrading
Module load order: if your app declares @KoinApplication(modules = [AppModule::class]) and AppModule defines a binding that is also defined in a @Configuration-discovered dependency module, the app's binding now wins (previously, the dep overrode the app). This is the documented last-wins semantic applied correctly — but if you were relying on the previous behaviour, reorder via an explicit modules = [Dep::class, AppModule::class] list to control load order precisely.
Contributors
Code contributions this release:
- @flaringapp — PR #23: cross-module
@Single(binds=[...])provider functions - @wjz2001 — PR #25: cross-module scan hints without
@Configuration
Ongoing contributors (carried over from RC1):
- Arnaud Giuliani — project lead
- @JellyBrick — caching optimisations (PR #5), Gradle release signing fix
- Kevin Chiu — Gradle plugin package fix
- Youssef Shoaib — runtime annotations provider, build improvements
Issue reporters — the reproductions were excellent, thank you:
- #17 — @gael-ft (Arrow
Raise.bind()crash) - #18 — @alex-z0, @corneloaie (generic iOS crash)
- #20 — @norbertsitko, @tusxxx, @rdietrichberlin (duplicate callsite hints, plus the
setHintPackageworkaround that informed the fix) - #22 — @flaringapp (cross-module
binds) - koin#2402 — @jmartinMone (module load order)
And Francois Dabonot (Kotzilla Slack), whose migration feedback drove the missing-artifact compile error introduced in RC2.3.
1.0.0-RC1
Koin Compiler Plugin 1.0.0-RC1
A native Kotlin Compiler Plugin for Koin dependency injection. Inline compile-time transformations — no generated Kotlin files to manage.
Replaces Koin Annotations (KSP) with a K2 compiler plugin that transforms DSL calls and processes annotations directly in the FIR/IR pipeline. Full Kotlin Multiplatform support.
Requires: Kotlin 2.3.x+ (K2) | Koin 4.2.1+
Features
DSL Transformations
Write idiomatic Koin DSL — the plugin resolves constructors at compile time.
| DSL Call | Description |
|---|---|
single<T>(), factory<T>(), viewModel<T>(), worker<T>(), scoped<T>() |
Definition with automatic constructor resolution |
create(::T) |
Constructor reference in scopes |
startKoin<T>(), koinApplication<T>(), module<T>() |
Application and module loading |
modules(vararg KClass) |
Multi-module loading |
Annotations
Full annotation-driven DI as an alternative (or complement) to DSL.
Definitions — on classes, @Module functions, or top-level functions:
@Singleton, @Factory, @Scoped, @KoinViewModel, @KoinWorker
Modules:
@Module, @ComponentScan, @Configuration (with label-based grouping)
Parameters:
@Named, @Qualifier (string and type), @InjectedParam, @Property, @PropertyValue, @ScopeId, @Provided
Compile-Time Safety
Detect missing or mismatched dependencies at build time — not at runtime.
| Level | Scope |
|---|---|
| A1 | Per-module: local definitions + explicit includes |
| A2 | Configuration group: @Configuration siblings share definitions |
| A3 | Full graph: all modules assembled via startKoin<T>() |
| A4 | Call-site: get<T>(), inject<T>(), koinViewModel<T>() |
| B | DSL modules: single<T>(), factory<T>() in hand-written modules |
| C | Cross-module: definitions from dependency JARs via hint functions |
| D | Properties: @Property/@PropertyValue key matching |
Automatically skipped: nullable params, @InjectedParam, @Provided, @ScopeId, Scope params, default values, List<T>, Android framework types (Context, Application, SavedStateHandle, etc.)
Cross-Module Discovery
Definitions, qualifiers, scopes, and bindings propagate across Gradle modules via lightweight hint functions. No runtime reflection, no classpath scanning.
Kotlin Multiplatform
Full KMP support — JVM, JS, Wasm, Native. Dramatically simplified setup compared to the KSP-based approach (no per-target wiring).
What's New in 1.0.0-RC1
Since 0.6.2:
- Fixed missing binding detection during compile-time safety validation
Since 0.4.x:
@ScopeId— inject dependencies from named or typed scopesScopeparameter injection — pass the Koin scope receiver directly@PropertyValue/@Propertyvalidation with warnings for missing defaults@Providedannotation — mark types as externally available, skip safety validationmodule<T>()andmodules(vararg KClass)DSL interceptionbinds=[]respected to suppress auto-binding (#12)- Actualized IR return types for Wasm/JS targets (#11)
- Performance: cached
referenceFunctions, batched hint file generation
Getting Started
// build.gradle.kts
plugins {
id("io.insert-koin.compiler.plugin") version "1.0.0-RC1"
}
koinCompiler {
compileSafety = true // Compile-time dependency validation (default)
skipDefaultValues = true // Use Kotlin defaults instead of DI resolution (default)
unsafeDslChecks = true // Validate create() lambda safety (default)
userLogs = true // Component detection logs
debugLogs = false // Verbose internal logs
}Migrating from Koin Annotations (KSP)
- Replace
ksp("io.insert-koin:koin-ksp-compiler:...")with the Gradle plugin above - Remove
ksp { }configuration blocks - Delete generated
*Module.ktfiles — the plugin transforms inline - See the full Migration Guide for details
Contributors
- Arnaud Giuliani — project lead
- JellyBrick — caching optimizations (PR #5), Gradle release signing fix
- Kevin Chiu — Gradle plugin package fix
- Youssef Shoaib — runtime annotations provider, build improvements
0.6.2
Koin Compiler Plugin 0.6.2
Compatibility: Kotlin 2.3.20 · Koin 4.2.1-RC1
Bug Fixes
-
Fix #11 — WASM/JS type actualization — Actualize IR return types for generic Koin calls (
Scope.get<T>(),getOrNull<T>(),inject<T>(),getAll<T>(),ParametersHolder.get<T>()). Preventsunbound IrTypeParameterSymbolImplcrashes on WASM/JS/Native targets. (3d9d2d8) -
Fix #12 — Delegation pattern auto-binding — Respect
binds = []to suppress auto-binding. Classes using the delegation pattern (class Decorated(val delegate: MyService) : MyService) with@Singleton(binds = [])no longer cause recursive resolution stack overflows. (78f477c)
Performance
- Cache
referenceFunctions()lookups — Avoid repeated expensive compiler API calls across modules. SameCallableIdqueried N times → 1 real lookup + (N-1) O(1) cache hits. - Cache
@Configurationmodule discovery —discoverConfigurationModulesFromHints()results cached by label set. 10 modules with same labels → 1 discovery instead of 10. - Cache
modulesByFqNamemap — Built once, reused across all module validations. - Batch hint IrFile creation — One
IrFileper module instead of per definition, reducing synthetic file count from O(definitions) to O(modules).
Caching optimizations inspired by @JellyBrick (PR #5) 🙏
Compile Safety
- Phase 3.6 guard — Cross-module call-site hint validation now only runs when a full graph has been assembled (via
@KoinApplication). Prevents false positives in library modules without full graph visibility.
Build
- Gradle signing fix — Only apply signing plugin when
IS_RELEASEis set (PR #4).
0.6.1
Koin Compiler Plugin 0.6.1
Release date: 2026-03-30
New Features
module<T>() and modules(vararg KClass) APIs
Load @Module classes without referencing generated code directly. The compiler plugin intercepts these calls and transforms them at compile time. (#14)
startKoin {
module<NetworkModule>()
modules(DataModule::class, CacheModule::class)
}Requires Koin 4.2.1-beta-1+
@ScopeId Parameter Support
Resolve dependencies from named Koin scopes. Generates getScope("id").get<T>(). Compile safety skips @ScopeId parameters. (#2)
@Factory
class ProfileService(@ScopeId(name = "user_session") val session: UserSession)
// Generates: ProfileService(scope.getScope("user_session").get())@Provided on Parameters
Previously only worked on classes. Now also works on individual constructor parameters to skip compile safety for that specific parameter. (#7)
@Singleton
class MyService(@Provided val ctx: PlatformContext)Scope Parameter Injection
Parameters of type org.koin.core.scope.Scope are automatically injected with the scope receiver. No annotation needed.
@Scoped
class ScopedService(val scope: Scope)
// Generates: ScopedService(scope)@Property/@PropertyValue Validation
Warns at compile time when @Property("key") has no matching @PropertyValue("key") default.
@PropertyValue("api.timeout")
val defaultTimeout = 30
@Factory
class ApiClient(@Property("api.timeout") val timeout: Int) // OK
@Factory
class Other(@Property("missing.key") val value: String) // WARNINGDSL Compile Safety Improvements
- Module reachability validation — Tracks which DSL modules are loaded via
modules()andincludes(). Reports compile errors for definitions in unreachable modules. bind()operator support — Explicitbind(Interface::class)is now tracked for DSL definitions. Auto-binding of supertypes removed for DSL path (matches Koin runtime behavior).create(::function)hints — Provider-only definitions fromcreate(::function)now generate cross-module hints withproviderOnlyflag.- Qualifier propagation in DSL hints —
@Named,@Qualifier, and type qualifiers are now encoded in DSL cross-module hints. - Call-site detection —
by inject()andby viewModel()property delegates in class bodies are now detected for A4 validation.
Bug Fixes
- Fix qualifier propagation in function definition calls
- Fix
@Monitortracing: warn if Kotzilla SDK library is missing - Fix DSL
bind()not being tracked — removingbindnow correctly triggers compile error - Fix
create(::function)not producing cross-module DSL hints - Fix FIR module data null for external library types (e.g., DataStore, CoroutineDispatcher)
- Fix cross-module qualifier encoding with dots in names
Breaking Changes
- DSL auto-binding removed — DSL definitions (
single<T>(),factory<T>()) no longer auto-bind to supertypes. Use explicitbind(Interface::class)to register secondary types. This matches Koin runtime behavior.
Compatibility
| Dependency | Version |
|---|---|
| Koin | 4.2.1-beta-1+ (for module<T>() API), 4.2.0-RC2+ (for other features) |
| Kotlin | 2.3.x+ (K2 compiler required) |
Resolved Issues
0.4.0
Native Kotlin Compiler Plugin for Koin — K2 Required (Kotlin 2.3.x+) | Koin 4.2.0-RC2+
Highlights
Compile-Time Dependency Safety is the headline feature of 0.4.0. The plugin now validates your entire dependency graph at compile time, catching missing bindings before they become runtime crashes.
New Features
Compile-Time Safety Checks (compileSafety = true, on by default)
Multi-layered validation that progressively widens visibility:
- A1 — Per-Module: validates definitions within a single @module plus its explicit includes
- A2 — Configuration Groups: modules sharing a @configuration label are validated together
- A3 — Full Graph (startKoin): validates the complete assembled graph when using @KoinApplication
- A4 — Call-Site Validation: checks get(), inject(), and koinViewModel() call sites against the known graph
- B — DSL Definitions: single(), factory(), etc. participate in the safety graph alongside annotation-based definitions
- C — Cross-Gradle-Module: definitions from dependency JARs are discovered via hint functions
- C2 — Full Hint Metadata: cross-module function hints now carry qualifier, scope, and binding information
Catches missing non-nullable dependencies, unresolved Lazy, qualifier mismatches, and cross-scope errors at compile time. Nullable params, @InjectedParam, @Property, List, and defaulted
parameters are safely skipped.
@provided Annotation
Mark types as externally available at runtime (e.g., platform types not declared as Koin definitions). Safety checks will skip them.
@Provided
class ExternalService // provided by framework at runtime
@Singleton
class MyService(val ext: ExternalService) // no errorAndroid Framework Whitelist
Common Android types are automatically whitelisted — no @provided needed:
Context, Activity, Application, Fragment, SavedStateHandle, WorkerParameters
@monitor Annotation
Function interception for logging and performance monitoring:
@Monitor
class MyService {
fun fetchData(): Data { ... } // calls intercepted with timing/logging
}skipDefaultValues Option (default: true)
Parameters with Kotlin default values are no longer injected from the DI container by default. Annotated and nullable parameters are still resolved normally.
class Service(val a: A, val name: String = "default")
single<Service>()
// Generated: Service(scope.get()) — name uses Kotlin default
Incremental Compilation Support
Dirty marker / IC recompilation detection ensures the plugin cooperates correctly with Kotlin's incremental compilation.
KMP Improvements
- Full hint function generation for JVM, JS, and Wasm targets
- @deprecated(HIDDEN) on generated hints to avoid polluting IDE autocomplete
- Fix for @configuration and @componentscan detection in multi-target builds
- KLIB workaround extended to JS and Wasm targets
Configuration
koinCompiler {
compileSafety = true // Compile-time dependency validation (default: true)
unsafeDslChecks = true // Validate create() is sole lambda instruction (default: true)
skipDefaultValues = true // Skip injection for defaulted params (default: true)
userLogs = true // Component detection logs
debugLogs = true // Verbose internal logs
}Compatibility
- Kotlin: 2.3.x+ (K2 compiler required)
- Koin: 4.2.0-RC2+