Problem
sbt-scalafmt 2.6.0 fails with corrupted class path when it is consumed transitively through another sbt plugin, rather than declared directly in a project's plugins.sbt.
This works fine:
// project/plugins.sbt
addSbtPlugin("org.scalameta" %% "sbt-scalafmt" % "2.6.0")
This fails:
// my-sbt-plugin publishes with sbt-scalafmt 2.6.0 as a dependency
// downstream project's plugins.sbt
addSbtPlugin("com.example" %% "my-sbt-plugin" % "1.0.0")
// sbt-scalafmt 2.6.0 is resolved transitively
Error
[error] (Compile / scalafmtCheck) org.scalafmt.sbt.ScalafmtSbtReporter$ScalafmtSbtError:
scalafmt: [v3.11.0] corrupted class path:
[/path/to/.cache/coursier/v1/https/.../scalafmt-core_2.13-3.11.0.jar ...]
Root Cause
The 2.6.0 release (changelog) changed from scalafmt-dynamic's own isolated jar downloader to sbt's native dependency resolution ("Implement scalafmt-dynamic downloader using sbt").
When sbt-scalafmt is bundled inside another sbt plugin and consumed transitively:
- scalafmt-core 3.11.0 classes end up on the parent sbt classloader (via the host plugin's transitive dependencies)
- scalafmt-dynamic also loads scalafmt-core into its own isolated classloader
- The classloader validation in scalafmt-dynamic sees the same classes on both classloaders and fails with "corrupted class path"
When declared directly in plugins.sbt, the classes only appear on one classloader, so validation passes. This is why the issue only manifests in the transitive case.
Steps to Reproduce
- Create an sbt plugin that depends on sbt-scalafmt 2.6.0:
// my-plugin/build.sbt
sbtPlugin := true
addSbtPlugin("org.scalameta" %% "sbt-scalafmt" % "2.6.0")
- Publish that plugin locally
- In a downstream project, consume the plugin transitively:
// downstream/project/plugins.sbt
addSbtPlugin("com.example" %% "my-plugin" % "1.0.0")
- Add a
.scalafmt.conf with version = "3.11.0"
- Run
sbt scalafmtCheck → fails with "corrupted class path"
Environment
- sbt 1.12.10
- Scala 2.13
- Java: Amazon Corretto 25
useCoursier := false (Ivy resolution), though likely also affects Coursier
- sbt-scalafmt 2.6.0 with scalafmt 3.11.0
Workaround
Revert to sbt-scalafmt 2.5.6, which uses the old isolated downloader approach and does not conflict with transitive classloading.
Problem
sbt-scalafmt 2.6.0 fails with
corrupted class pathwhen it is consumed transitively through another sbt plugin, rather than declared directly in a project'splugins.sbt.This works fine:
This fails:
Error
Root Cause
The 2.6.0 release (changelog) changed from scalafmt-dynamic's own isolated jar downloader to sbt's native dependency resolution ("Implement scalafmt-dynamic downloader using sbt").
When sbt-scalafmt is bundled inside another sbt plugin and consumed transitively:
When declared directly in
plugins.sbt, the classes only appear on one classloader, so validation passes. This is why the issue only manifests in the transitive case.Steps to Reproduce
.scalafmt.confwithversion = "3.11.0"sbt scalafmtCheck→ fails with "corrupted class path"Environment
useCoursier := false(Ivy resolution), though likely also affects CoursierWorkaround
Revert to sbt-scalafmt 2.5.6, which uses the old isolated downloader approach and does not conflict with transitive classloading.