--- title: error / spotbugs-commons-lang3-bom-downgrade-noclassdef-2026-06-20 source_type: error-note status: raw related_branches: [feature-static-analysis-quality-contract] related_projects: [ca-skeleton] tags: [error, ca-skeleton, spotbugs, gradle, spring-dependency-management, bom, static-analysis, java21] created: 2026-06-20 status_label: resolved --- # error: spotbugs-commons-lang3-bom-downgrade-noclassdef-2026-06-20 > Layer: `raw/errors/` — SpotBugs 4.10.2 analysis worker crash caused by the Spring Boot BOM > downgrading commons-lang3 on the `spotbugs` tool configuration. The classic "dependency > management leaks onto a tool classpath" trap. ## Parent / 부모 - [[raw/branch-notes/feature-static-analysis-quality-contract]] — D3/D4 SpotBugs + FindSecBugs wiring. Hit while verifying Claim "SpotBugs 6.5.6(core 4.10.2) on Gradle 9.0.0". ## 증상 / Symptom - 발생 컨텍스트: `./gradlew spotbugsMain` 을 처음 실행하자 8/10 모듈에서 `spotbugsMain FAILED` + `SpotBugs ended with exit code 4`. 버그가 발견된 게 아니라 **분석 자체가 죽음** — `build/reports/spotbugs/*.xml` 리포트가 아예 생성 안 됨(crash before report). - `--console=plain` 로 보니 root cause: ```text edu.umd.cs.findbugs.ba.AnalysisException: Exception was thrown during analysis Caused by: java.lang.NoClassDefFoundError: org/apache/commons/lang3/Strings Caused by: java.lang.ClassNotFoundException: org.apache.commons.lang3.Strings at edu.umd.cs.findbugs.ba.vna.ValueNumberFrameModelingVisitor.visitLDC(...) ``` - 재현 가능 여부: `always` (Spring Boot dependency-management + SpotBugs 4.10.2 조합에서 항상) ## 재현 절차 / Reproduction 1. Spring Boot `io.spring.dependency-management` 가 적용된 Gradle 멀티모듈 빌드에서 `com.github.spotbugs` 6.5.6 plugin + `spotbugs { toolVersion = '4.10.2' }` 적용. 2. `./gradlew spotbugsMain` 실행. 3. `NoClassDefFoundError: org.apache.commons.lang3.Strings` 로 분석 worker crash. ## 조사 단계 / Investigation log - `./gradlew :shared-contract:dependencies --configuration spotbugs | grep commons-lang3` → `org.apache.commons:commons-lang3:3.20.0 -> 3.17.0`. SpotBugs 가 요구하는 3.20.0 이 BOM 에 의해 3.17.0 으로 강등됨. - BOM 확인: `spring-boot-dependencies-3.5.15.pom` → `3.17.0`. - SpotBugs POM 확인: `spotbugs-4.10.2.pom` → `commons-lang3` `3.20.0` (`org.apache.commons.lang3.Strings` 는 commons-lang3 3.18.0 에서 추가됨 → 3.17.0 엔 없음 → NoClassDefFound). - `resolutionStrategy.force 'org.apache.commons:commons-lang3:3.20.0'` 를 `spotbugs` 설정에 적용 → **효과 없음**. 재확인 시 여전히 `3.20.0 -> 3.17.0`. `io.spring.dependency-management` 가 `force` 를 덮어쓴다. - production main 코드에서 `org.apache.commons.lang3` import 0건 확인 → commons-lang3 는 사실상 SpotBugs 도구 classpath 에만 존재 → 버전 상향의 런타임 영향 없음. ## 근본 원인 / Root cause - `io.spring.dependency-management` 는 BOM 의 managed version 을 **모든 configuration** 에 적용한다 — 런타임 classpath 뿐 아니라 `spotbugs`(SpotBugs 분석 worker) 같은 도구 전용 configuration 의 transitive 까지. 그래서 SpotBugs 가 가져오려던 commons-lang3 3.20.0 이 BOM 의 3.17.0 으로 강등되고, 3.17.0 엔 SpotBugs 4.10.2 가 LDC 모델링에서 참조하는 `org.apache.commons.lang3.Strings` 가 없어 worker 가 crash 한다. - `resolutionStrategy.force` 가 안 먹힌 이유: dependency-management 플러그인이 자체 resolution 액션으로 managed version 을 강제하며, 이게 force 보다 우선한다. ## 해결 / Resolution - BOM 이 관리하는 버전 property 자체를 override (Spring 공식 메커니즘): ```groovy ext['commons-lang3.version'] = '3.20.0' ``` subprojects 블록에 두면 `dependencyManagement` 가 BOM placeholder 를 3.20.0 으로 해석 → `spotbugs` 설정의 commons-lang3 가 3.20.0 으로 resolve → crash 해소. - 재확인: `dependencies --configuration spotbugs` 에서 `commons-lang3:3.20.0`(강등 화살표 사라짐), `./gradlew spotbugsMain` → 분석 정상 수행(이후 EI/보안 finding 은 reportLevel·exclude 로 별도 처리). - production main 이 commons-lang3 를 안 쓰므로 글로벌 property override 의 실질 영향은 SpotBugs 도구 classpath 한정. 3.17→3.20 은 commons-lang3 3.x 의 backward-compatible minor 상향. ## 교훈 / Lessons - **Spring dependency-management 는 도구 전용 configuration(spotbugs/checkstyle/errorprone 등)에도 BOM 을 적용한다.** 도구가 BOM 보다 최신 transitive 를 요구하면 조용히 강등되어 `NoClassDefFoundError`/`NoSuchMethodError` 로 런타임에 터진다. - **`resolutionStrategy.force` 는 dependency-management 를 못 이긴다.** 도구 classpath 버전을 고치려면 `ext['.version']` 로 **managed version property 자체를 override** 하는 게 정공법. - **SpotBugs "exit code 4" + 리포트 부재 = 분석 crash(버그 발견 아님).** `--console=plain` 로 `AnalysisException`/`Caused by` 를 먼저 확인할 것. exit code 1 이 "버그 발견"이고 4 류는 analysis error 신호. - 버전 충돌 디버깅은 `gradlew :dependencies --configuration ` 로 강등 화살표(`X -> Y`)를 직접 본다. ## 관련 / Related - [[raw/branch-notes/feature-static-analysis-quality-contract]] - [[raw/blog-topics/gradle9-java21-static-analysis-baseline-2026-06-20]]