Skip to content

Binary-incompatible changes in Settings (1.22.0) and EventPipeline (1.25.0) crash pre-built downstream SDKs at runtime #311

Description

@mahmoud-elmorabea

TL;DR

Two recent releases added parameters to the primary constructors of public classes:

  • 1.22.0 added a 7th field to Settings (autoInstrumentation).
  • 1.25.0 added two trailing parameters to EventPipeline (httpConfig, timeProvider).

In Kotlin, adding a parameter to a primary constructor — even with a default value — is a binary-incompatible change. The synthetic …$default(...) constructor that callers reference at the bytecode level disappears, and the new one has a different JVM descriptor.

Source-level callers that recompile against the new version are fine. Pre-built AARs/JARs (third-party SDKs that depend on analytics-kotlin and ship compiled artifacts to their customers) crash at app launch with NoSuchMethodError whenever the host application's Gradle graph resolves Segment to a version newer than the one those SDKs were compiled against.

Who is affected

  • Third-party SDKs that wrap or extend analytics-kotlin and publish a pre-built AAR/JAR — every consumer of those SDKs whose app resolves Segment ≥1.22.0 (for Settings) or ≥1.25.0 (for EventPipeline).
  • End-user Android apps that include such an SDK alongside another transitive dependency that requests a newer Segment via 1.22.0+ or similar.
  • Apps that depend on analytics-kotlin directly and recompile against the new version — not affected; the Kotlin compiler simply emits bytecode against the new descriptor.
  • Apps that hold Segment back to the version their SDKs were compiled against (e.g., via a strict pin or lockfile) — not affected, but at the cost of giving up newer Segment fixes.

The asymmetry is the key point: a Kotlin source-compatible change (default-valued parameter) is not binary-compatible. Downstream SDK vendors cannot fix this for their already-published artifacts — the bytecode is frozen.

Minimal repro:

  1. Build any small Android library that depends on com.segment.analytics.kotlin:android:1.19.2 and constructs a Settings() (or any subset of its fields, leaving at least one defaulted).
  2. Publish it as an AAR (or just assemble and consume the local artifact).
  3. In a separate consumer Android app, depend on that AAR and force-resolve com.segment.analytics.kotlin:android to 1.25.0.
  4. Run the app → instant NoSuchMethodError on the Settings constructor reference.

The same pattern with an EventPipeline(analytics, key, writeKey, flushPolicies, apiHost) call site reproduces the 1.25.0-specific break.

Where each break landed

Break Class Commit First released in
Added 7th field autoInstrumentation to primary ctor core/.../Settings.kt adeb09a (#279, "Add auto instrumentation settings") 1.22.0
Added trailing httpConfig, timeProvider to primary ctor core/.../platform/EventPipeline.kt #300 ("Phase 4 — EventPipeline integration with smart retry system") 1.25.0

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions