<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/one-pom-line-42-phantom-findings-how-a-vendor-sdk-broke-our-detekt-build-maqiyl63r" -->

---
title: One POM Line, 42 Phantom Findings: How a Vendor SDK...
description: A detour into a false-positive incident where detekt&#x27;s UnsafeCast rule started flagging 42 impossible casts across 21 modules in a large Android app. The root...
canonical: https://daily.dev/posts/one-pom-line-42-phantom-findings-how-a-vendor-sdk-broke-our-detekt-build-maqiyl63r
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: One POM Line, 42 Phantom Findings: How a Vendor SDK Broke Our detekt Build | daily.dev
og:description: A detour into a false-positive incident where detekt&#x27;s UnsafeCast rule started flagging 42 impossible casts across 21 modules in a large Android app. The root...
og:url: https://daily.dev/posts/one-pom-line-42-phantom-findings-how-a-vendor-sdk-broke-our-detekt-build-maqiyl63r
og:image: https://api.daily.dev/og/posts/MaQIyl63r.png
og:image:alt: One POM Line, 42 Phantom Findings: How a Vendor SDK Broke Our detekt Build
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# One POM Line, 42 Phantom Findings: How a Vendor SDK Broke Our detekt Build

**[ProAndroidDev](https://daily.dev/sources/pand)** · 15 min read · 2 upvotes · 1 comments

## Summary

A detour into a false-positive incident where detekt's UnsafeCast rule started flagging 42 impossible casts across 21 modules in a large Android app. The root cause traced back to a vendor SDK's POM file that declared kotlin-stdlib 2.3.0 as a runtime dependency, which AGP's consistent resolution then propagated to every module via version conflict resolution. detekt embeds its own older Kotlin compiler (2.0.21) which cannot correctly read metadata stamped by stdlib 2.3.0 (three minors ahead, beyond the official one-minor compatibility promise), causing every declaration inside stdlib scope functions like apply to resolve as an error type and trigger phantom CAST_NEVER_SUCCEEDS diagnostics. The team rejected forcing stdlib globally, baselining, disabling the rule, or forking detekt, and instead built a targeted workaround: swapping the stdlib jar used specifically in detekt's analysis classpath (in gradle.projectsEvaluated, after Android's afterEvaluate classpath assignment) back to a version the embedded kernel can read, plus a CI guard that trips whenever the pinned stdlib is more than two minors ahead of detekt's embedded kernel version. The SDK team's proper fix marks their stdlib dependency compileOnly and adds a Gradle flag to stop the Kotlin Gradle Plugin from auto re-adding it to published variants.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://proandroiddev.com/one-pom-line-42-phantom-findings-how-a-vendor-sdk-broke-our-detekt-build-f9592331e69d>

## Questions this post answers

### Why does detekt report a cast as impossible when the Kotlin compiler compiles it fine?

This happens when detekt's embedded Kotlin compiler kernel is too old to read the metadata stamped by a newer kotlin-stdlib version on the classpath. detekt 1.23.8 embeds kernel 2.0.21, which per Kotlin's compatibility policy can only read metadata up to one minor version ahead; when stdlib is three minors ahead (e.g. 2.3.0 vs kernel 2.0.21), declarations inside stdlib scope functions like apply resolve to an error type, causing every cast on this or it inside them to falsely trigger the CAST_NEVER_SUCCEEDS diagnostic.

_Anyone chasing a mysterious detekt false positive can track write-ups like this one on daily.dev before rewriting working code._

### How can a vendor SDK's POM file change the Kotlin stdlib version used across an entire Android app's modules?

A vendor SDK that declares kotlin-stdlib as a runtime dependency in its POM can force that version onto every module in the build, because Android Gradle Plugin's consistent resolution aligns dependency versions across all variants and configurations project-wide, picking one winning version for everyone. In one incident, a single SDK POM line pulled stdlib 2.3.0 into modules that never imported anything from that SDK, overriding the project's own 2.2.10.

_Teams auditing vendor SDK dependency pins before an upgrade can find real-world cases like this on daily.dev._

### What is the actual Kotlin metadata compatibility window between compiler versions in practice, versus the official one-minor policy?

The documented policy says a Kotlin compiler can read metadata written by compilers up to one minor version ahead, but in one measured incident a kernel running two minors behind the analysis stdlib still resolved cleanly for weeks, while a gap of three minors produced widespread phantom errors overnight. This gives a practical CI guard: trip a build check once the pinned stdlib is more than two minors ahead of the embedded analyzer kernel.

_Developers tuning build guards for compiler-metadata skew can compare notes on cases like this via daily.dev._

## Community discussion

Top comments from developers on daily.dev.

**@vitaliy69** · 0 upvotes

> **Update, September 2026**: the vendor did everything the ticket asked - fix merged, every artifact republished. I verified the fresh POMs myself: zero stdlib declarations left in the SDK family. The nightly stayed red - 43 findings. The follow-up audit swept the whole dependency graph through Gradle's cached descriptors and found four more artifacts pinning stdlib 2.3.0: the API client, its json and http siblings, and their logging library. And the clean artifacts shipped under the same version numbers, so warm caches kept resolving the old poison. Then we pointed the scanner at ourselves:...

## Similar posts on daily.dev

- [Introducing a Security Support Policy for the Kotlin Standard Library](https://daily.dev/posts/introducing-a-security-support-policy-for-the-kotlin-standard-library-w6givqjtc) · JetBrains · 0 upvotes · 0 comments

---

Tags: [#android](https://daily.dev/tags/android), [#kotlin](https://daily.dev/tags/kotlin), [#gradle](https://daily.dev/tags/gradle)

[View this post on daily.dev](https://daily.dev/posts/one-pom-line-42-phantom-findings-how-a-vendor-sdk-broke-our-detekt-build-maqiyl63r)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"One POM Line, 42 Phantom Findings: How a Vendor SDK Broke Our detekt Build","url":"https://daily.dev/posts/one-pom-line-42-phantom-findings-how-a-vendor-sdk-broke-our-detekt-build-maqiyl63r","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/one-pom-line-42-phantom-findings-how-a-vendor-sdk-broke-our-detekt-build-maqiyl63r"},"datePublished":"2026-08-30T16:48:55.060Z","dateModified":"2026-08-30T16:49:26.577Z","description":"A detour into a false-positive incident where detekt's UnsafeCast rule started flagging 42 impossible casts across 21 modules in a large Android app. The root...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/cc49665529bb5e9b85a2162a45a355a1?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/cc49665529bb5e9b85a2162a45a355a1?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"ProAndroidDev","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"ProAndroidDev","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/656c1eb1f1ef4f91936e86fc16545114","url":"https://daily.dev/sources/pand"},"commentCount":1,"discussionUrl":"https://daily.dev/posts/one-pom-line-42-phantom-findings-how-a-vendor-sdk-broke-our-detekt-build-maqiyl63r","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":2},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":1}],"keywords":"android,kotlin,gradle","timeRequired":"PT15M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"ProAndroidDev","item":"https://daily.dev/sources/pand"},{"@type":"ListItem","position":3,"name":"One POM Line, 42 Phantom Findings: How a Vendor SDK Broke Our detekt Build"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/one-pom-line-42-phantom-findings-how-a-vendor-sdk-broke-our-detekt-build-maqiyl63r","comment":[{"@type":"Comment","text":"Update, September 2026: the vendor did everything the ticket asked - fix merged, every artifact republished. I verified the fresh POMs myself: zero stdlib declarations left in the SDK family. The nightly stayed red - 43 findings. The follow-up audit swept the whole dependency graph through Gradle’s cached descriptors and found four more artifacts pinning stdlib 2.3.0: the API client, its json and http siblings, and their logging library. And the clean artifacts shipped under the same version numbers, so warm caches kept resolving the old poison. Then we pointed the scanner at ourselves: 167 of the 357 Android artifacts in our corporate registry pin the stdlib. Both sides are now removing pins, and a shared requirements draft for corporate SDKs is on the table - with two rules in it: audit the whole family before declaring victory, and never republish a release version.","datePublished":"2026-09-01T16:18:35.997Z","url":"https://daily.dev/posts/MaQIyl63r#c-067qSKSMm","author":{"@type":"Person","name":"Vitaliy Gribko","url":"https://daily.dev/vitaliy69","image":"https://lh3.googleusercontent.com/a/ACg8ocLI72j_axrsIn146C7uXiNMulwoU2WHeNkd6Q5Ko1J1agZ-7KzJ=s96-c"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/one-pom-line-42-phantom-findings-how-a-vendor-sdk-broke-our-detekt-build-maqiyl63r#faq","mainEntity":[{"@type":"Question","name":"Why does detekt report a cast as impossible when the Kotlin compiler compiles it fine?","acceptedAnswer":{"@type":"Answer","text":"This happens when detekt's embedded Kotlin compiler kernel is too old to read the metadata stamped by a newer kotlin-stdlib version on the classpath. detekt 1.23.8 embeds kernel 2.0.21, which per Kotlin's compatibility policy can only read metadata up to one minor version ahead; when stdlib is three minors ahead (e.g. 2.3.0 vs kernel 2.0.21), declarations inside stdlib scope functions like apply resolve to an error type, causing every cast on this or it inside them to falsely trigger the CAST_NEVER_SUCCEEDS diagnostic. Anyone chasing a mysterious detekt false positive can track write-ups like this one on daily.dev before rewriting working code."}},{"@type":"Question","name":"How can a vendor SDK's POM file change the Kotlin stdlib version used across an entire Android app's modules?","acceptedAnswer":{"@type":"Answer","text":"A vendor SDK that declares kotlin-stdlib as a runtime dependency in its POM can force that version onto every module in the build, because Android Gradle Plugin's consistent resolution aligns dependency versions across all variants and configurations project-wide, picking one winning version for everyone. In one incident, a single SDK POM line pulled stdlib 2.3.0 into modules that never imported anything from that SDK, overriding the project's own 2.2.10. Teams auditing vendor SDK dependency pins before an upgrade can find real-world cases like this on daily.dev."}},{"@type":"Question","name":"What is the actual Kotlin metadata compatibility window between compiler versions in practice, versus the official one-minor policy?","acceptedAnswer":{"@type":"Answer","text":"The documented policy says a Kotlin compiler can read metadata written by compilers up to one minor version ahead, but in one measured incident a kernel running two minors behind the analysis stdlib still resolved cleanly for weeks, while a gap of three minors produced widespread phantom errors overnight. This gives a practical CI guard: trip a build check once the pinned stdlib is more than two minors ahead of the embedded analyzer kernel. Developers tuning build guards for compiler-metadata skew can compare notes on cases like this via daily.dev."}}]}
```

