Regression in 8.6.0: groovyGradle/greclipse fails with "roundtripStateInternal or equalityStateInternal should be non-null" during input fingerprinting
- 主要言語
- Java
- スター
- 5.7k
- フォーク
- 561
- 平均マージ
- 1日 14時間
- マージ済み PR(30日)
- 43
説明
## Summary
Starting with **Spotless 8.6.0**, the `groovyGradle { greclipse() }` task fails with:
```
If the initializer was null, then one of roundtripStateInternal or equalityStateInternal should be non-null, and neither was
```
This is a **regression**: **8.4.0 and 8.5.0 work** with the exact same project/config/Gradle version; **8.6.0 fails**. Pinning back to 8.5.0 is our current workaround.
It is closely related to (and may be the same root cause as) #2927 and #2502, but we can pinpoint it to the 8.5.0 → 8.6.0 bump, and it does **not** require the configuration cache (it happens with config cache disabled / not configured).
## Environment
- Spotless Gradle plugin **8.6.0** (8.4.0 and 8.5.0 OK)
- Gradle **9.5.1**
- JDK 26 (Temurin) — also reproduces on the same setup regardless
- Formatter: `groovyGradle { greclipse() }` (default greclipse → Groovy-Eclipse `5.7.0`/`e4.35`, Groovy `5.0.0`)
- Configuration cache: **not enabled**
- Build cache: disabled on the command line (`--no-build-cache`) — does **not** affect the outcome
## Spotless config (relevant part)
```groovy
spotless {
groovyGradle {
toggleOffOn()
greclipse()
leadingTabsToSpaces()
}
}
```
## How to reproduce
Run on a **clean Gradle user home** (e.g. a fresh CI container where greclipse is provisioned from scratch):
```
./gradlew spotlessCheck --no-daemon --no-build-cache --stacktrace
```
`:spotlessGroovyGradle` fails. It does **not** reproduce against a warm local cache where greclipse is already provisioned — which suggests the failure is tied to the freshly-built greclipse step state rather than to any formatting violation.
## Stack trace
The exception is thrown while Gradle **fingerprints the task's input properties** (before execution), when it calls `hashCode()` on Spotless's `ConfigurationCacheHackList`:
```
Caused by: java.lang.IllegalStateException: If the initializer was null, then one of roundtripStateInternal or equalityStateInternal should be non-null, and neither was
at com.diffplug.spotless.FormatterStepSerializationRoundtrip.writeObject(FormatterStepSerializationRoundtrip.java:74)
at com.diffplug.spotless.FormatterStepSerializationRoundtrip$HackClone.writeObject(FormatterStepSerializationRoundtrip.java:121)
at com.diffplug.spotless.ConfigurationCacheHackList.writeObject(ConfigurationCacheHackList.java:78)
at com.diffplug.spotless.LazyForwardingEquality.toBytes(LazyForwardingEquality.java:110)
at com.diffplug.spotless.FormatterStepEqualityOnStateSerialization.lambda$serializedState$0(FormatterStepEqualityOnStateSerialization.java:95)
at com.diffplug.spotless.ThrowingEx.get(ThrowingEx.java:67)
at com.diffplug.spotless.FormatterStepEqualityOnStateSerialization.serializedState(FormatterStepEqualityOnStateSerialization.java:95)
at com.diffplug.spotless.FormatterStepEqualityOnStateSerialization.hashCode(FormatterStepEqualityOnStateSerialization.java:75)
at com.diffplug.spotless.FormatterStepSerializationRoundtrip$HackClone.hashCode(FormatterStepSerializationRoundtrip.java:142)
at com.diffplug.spotless.ConfigurationCacheHackList.hashCode(ConfigurationCacheHackList.java:151)
at org.gradle.internal.execution.impl.DefaultInputFingerprinter$InputCollectingVisitor.visitInputProperty(DefaultInputFingerprinter.java:131)
at org.gradle.api.internal.tasks.execution.TaskExecution.visitMutableInputs(TaskExecution.java:336)
at org.gradle.internal.execution.impl.DefaultInputFingerprinter.fingerprintInputProperties(DefaultInputFingerprinter.java:77)
at org.gradle.internal.execution.steps.CaptureMutableStateBeforeExecutionStep.captureExecutionStateWithOutputs(CaptureMutableStateBeforeExecutionStep.java:124)
...
at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.executeMutable(LoadPreviousExecutionStateStep.java:36)
```
## Analysis / likely cause
- The failure is in `FormatterStepSerializationRoundtrip.writeObject` (line 74), which asserts that if `initializer == null` then one of `roundtripStateInternal` / `equalityStateInternal` must be non-null — and here **both are null**.
- It is reached via `ConfigurationCacheHackList.hashCode()` during Gradle's **input fingerprinting** (`DefaultInputFingerprinter` / `CaptureMutableStateBeforeExecutionStep`), i.e. before the task action runs — so it triggers for ordinary up-to-date checking, independent of the build cache and without the configuration cache.
- Because it only reproduces with a **freshly provisioned greclipse** (clean Gradle user home) and not against a warm cache, the greclipse step's state appears to be left without a computed `roundtripStateInternal`/`equalityStateInternal` (initializer already consumed) at the point Gradle serializes it for the fingerprint.
## Workaround
Pin the Spotless Gradle plugin to **8.5.0**.
Happy to provide a minimal reproducer repo or extra logs (`--info`/`--debug`) if helpful.
コントリビューションガイド
調査の方向性
Start by reproducing the failure with a clean Gradle user home using ./gradlew spotlessCheck --no-daemon --no-build-cache --stacktrace. Read FormatterStepSerializationRoundtrip.java, ConfigurationCacheHackList.java, and the related serialization classes along the shown stack trace. Done means the freshly provisioned greclipse task no longer fails during input fingerprinting.
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- groovy, java
- 領域
- build-system
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 静か
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 45/100