Home > Internal > Sessions > PR #269 Debugging Session


PR #269 Debugging Session: Firebase BOM Resolution Crisis

Date: 2025-11-18 (Week 28)

Duration: ~2 hours, 6 build attempts

Agent: Orchestrator (Agent O)

Outcome: ✅ Resolved - Stay on Firebase BOM 33.0.0, discovered KTX deprecation

Key Discovery: Firebase BOM 34.0.0+ removed all -ktx libraries (July 2025)

Session Overview

Emergency debugging session for PR #269 (Gradle dependency updates) that encountered build failures after updating Firebase BOM. What started as a simple dependency update spiraled into a deep investigation of Gradle version catalogs, BOM behavior, and Firebase’s deprecation of KTX modules.

Learning Value: This session demonstrates the importance of checking external dependency release notes when builds fail unexpectedly.


Initial Context

PR #269 Objective

Goal: Update Gradle dependencies to latest versions

Scope:

  • Update Compose BOM
  • Update Firebase BOM
  • Move Firebase dependencies to version catalog (better maintainability)
  • Update other Android dependencies

Expected Risk: Low - routine dependency updates


Build Attempt #1: Compose BOM Fix

Initial Problem

Copilot Alert:

Compose BOM version inconsistency detected

Changes Made

File: gradle/libs.versions.toml

[versions]
- compose-bom = "2024.06.00"
+ compose-bom = "2025.11.00"
 
[libraries]
- compose-bom = "androidx.compose:compose-bom:2024.06.00"  # String notation, version duplicated
+ compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }  # Object notation, version reference

File: app/build.gradle.kts

dependencies {
    implementation(platform(libs.compose.bom))  # Now uses version catalog
    // ... compose dependencies
}

Result

./gradlew clean build

Output:

> Task :app:processDebugManifest FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:processDebugManifest'.
> Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
   > Could not find com.google.firebase:firebase-analytics-ktx:.
     Required by:
         project :app
   > Could not find com.google.firebase:firebase-crashlytics-ktx:.
     Required by:
         project :app

Observation: Empty version strings (:firebase-analytics-ktx:. with nothing after the final :)

Analysis

  • Compose BOM fix didn’t cause this (Firebase issue)
  • Firebase BOM updated to 34.6.0 in same commit
  • Gradle can’t find versions for Firebase KTX libraries
  • Suspected version catalog syntax issue

Commit: 72bb86ce - fix: Use version catalog for Compose BOM and Firebase dependencies


Build Attempt #2: Firebase Version Catalog Syntax

Hypothesis

Version catalog syntax for Firebase might be incorrect. Maybe BOM-managed dependencies need different syntax.

Investigation

Checked version catalog:

[versions]
firebase-bom = "34.6.0"
 
[libraries]
firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase-bom" }
firebase-auth-ktx = { group = "com.google.firebase", name = "firebase-auth-ktx" }  # No version - BOM provides it
firebase-analytics-ktx = { group = "com.google.firebase", name = "firebase-analytics-ktx" }

Build script:

dependencies {
    implementation(platform(libs.firebase.bom))
    implementation(libs.firebase.auth.ktx)
    implementation(libs.firebase.analytics.ktx)
}

Researched:

  • Gradle documentation on version catalogs + BOMs
  • Pattern matched official examples

Conclusion

Syntax looks correct according to Gradle docs. BOM should provide versions for individual libraries.

Result

No changes made - syntax appeared correct.


Build Attempt #3: Add Missing Library to Catalog

Hypothesis

Maybe firebase-crashlytics-ktx was missing from version catalog?

Changes Made

File: gradle/libs.versions.toml

[libraries]
firebase-auth-ktx = { group = "com.google.firebase", name = "firebase-auth-ktx" }
firebase-analytics-ktx = { group = "com.google.firebase", name = "firebase-analytics-ktx" }
+ firebase-crashlytics-ktx = { group = "com.google.firebase", name = "firebase-crashlytics-ktx" }

Result

./gradlew clean build

Same error:

> Could not find com.google.firebase:firebase-analytics-ktx:.
> Could not find com.google.firebase:firebase-crashlytics-ktx:.

Analysis

Adding the catalog entry didn’t help. Both libraries still have empty versions.

Commit: e3fee2fd - fix: Add firebase-crashlytics-ktx to version catalog


Build Attempt #4: KSP Version Compatibility

Hypothesis

Noticed KSP version might be incompatible with Kotlin 2.2.21.

Investigation

Current versions:

  • Kotlin: 2.2.21
  • KSP: 2.2.0-2.0.2

KSP versioning scheme: {KSP version}-{Kotlin version}

Problem: KSP 2.2.0 might not work with Kotlin 2.2.21

Changes Made

File: gradle/libs.versions.toml

[versions]
- ksp = "2.2.0-2.0.2"
+ ksp = "2.2.21-1.0.29"  # Match Kotlin 2.2.21

Note: Later discovered this version doesn’t exist. Should be 2.2.21-2.0.4.

Result

./gradlew clean build

New error:

> Could not find com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.2.21-1.0.29.

Firebase errors still present (but hidden by KSP error)

Analysis

  1. KSP version doesn’t exist
  2. Even if it did, probably unrelated to Firebase issue
  3. Red herring

Commit: 9589d762 - fix: Update KSP to 2.2.21-1.0.29 for Kotlin 2.2.21 compatibility (WRONG VERSION)


Build Attempt #5: Downgrade Firebase BOM

Hypothesis

Maybe Firebase BOM 34.6.0 is too new? Try older 34.x version.

Changes Made

File: gradle/libs.versions.toml

[versions]
- firebase-bom = "34.6.0"
+ firebase-bom = "34.3.0"  # Slightly older

Additional Actions

./gradlew clean
rm -rf ~/.gradle/caches/  # Nuclear option - clear all Gradle caches
./gradlew build

Result

Same Firebase errors:

> Could not find com.google.firebase:firebase-analytics-ktx:.
> Could not find com.google.firebase:firebase-crashlytics-ktx:.

Analysis

  • BOM version doesn’t matter (tried 34.6.0, 34.3.0, both fail)
  • Cache clearing doesn’t help
  • Problem is deeper than version selection

Pattern emerging: All Firebase BOM 34.x versions fail with KTX libraries


Breakthrough: Compare with Main Branch

Investigation Method

Checkout main branch and examine working configuration:

git stash
git checkout main
cat app/build.gradle.kts | grep firebase

Discoveries

Main branch uses:

// File: app/build.gradle.kts
dependencies {
    // Hardcoded Firebase BOM version
    implementation(platform("com.google.firebase:firebase-bom:33.0.0"))
 
    // String literals, NOT version catalog references
    implementation("com.google.firebase:firebase-auth-ktx")
    implementation("com.google.firebase:firebase-firestore-ktx")
    implementation("com.google.firebase:firebase-analytics-ktx")
    implementation("com.google.firebase:firebase-crashlytics-ktx")
}

Key differences from PR branch:

  1. BOM version: 33.0.0 (not 34.x)
  2. Dependency style: String literals (not version catalog)
  3. Configuration: Hardcoded (not centralized)

Initial Conclusion

Maybe version catalog approach is incompatible with Firebase BOM?

Test: Try BOM 33.0.0 with Version Catalog

[versions]
firebase-bom = "33.0.0"  # Same as main
 
[libraries]
firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase-bom" }
firebase-auth-ktx = { group = "com.google.firebase", name = "firebase-auth-ktx" }
./gradlew clean build

Result:BUILD SUCCESSFUL

Revelation

It’s not the version catalog syntax - it’s the BOM version!

  • BOM 33.0.0 works with KTX libraries
  • BOM 34.x fails with KTX libraries
  • Something changed between 33.x and 34.x

Deep Investigation: Firebase Release Notes

Research Questions

  1. Why does Firebase BOM 34.x not provide versions for -ktx libraries?
  2. Is this intentional or a bug?
  3. What changed between BOM 33.x and 34.x?

Research Process

Searched:

  • Firebase Android release notes
  • Firebase BOM changelog
  • Google for “firebase ktx deprecated”
  • Stack Overflow threads

The Discovery

Firebase Release Notes (July 2025):

Firebase BoM v34.0.0

In July 2025, Firebase stopped releasing new versions of the KTX modules, and removed the KTX modules from the Firebase BoM (starting with BoM v34.0.0).

Migration: The Kotlin extensions are now included in the main modules. To use the Kotlin extensions, remove the -ktx suffix from your Firebase dependencies.

Example:

  • Before: com.google.firebase:firebase-auth-ktx
  • After: com.google.firebase:firebase-auth

All KTX APIs remain available in the main modules. No code changes required.

Implications

  1. Firebase deprecated KTX modules entirely
  2. BOM 34.0.0+ doesn’t include -ktx libraries
  3. This is intentional, not a bug
  4. Our version catalog syntax was correct all along
  5. We were referencing libraries that no longer exist in the BOM

Solution Decision

Options

Option A: Migrate to Firebase BOM 34.x (non-KTX)

  • Remove -ktx suffixes from all Firebase dependencies
  • Update version catalog entries
  • Test all Firebase functionality
  • Effort: Medium
  • Risk: Low (no code changes needed)
  • Timeline: Could be done now

Option B: Stay on Firebase BOM 33.0.0 (KTX)

  • Keep existing -ktx dependencies
  • Revert to hardcoded Firebase dependencies (match main)
  • Defer migration to future PR
  • Effort: Low
  • Risk: Very low (matches working main branch)
  • Timeline: Immediate

Decision: Option B (Stay on BOM 33.0.0)

Rationale:

  1. PR #269 goal is dependency updates, not Firebase migration
  2. BOM 33.0.0 is stable and working
  3. Minimize scope of PR (already complex)
  4. Can migrate to 34.x in dedicated future PR
  5. Unblock PR #269 and PR #271 (both blocked)

Build Attempt #6: Final Solution

Changes Made

File: app/build.gradle.kts

dependencies {
    // Revert to hardcoded Firebase dependencies matching main
-   implementation(platform(libs.firebase.bom))
-   implementation(libs.firebase.auth.ktx)
-   implementation(libs.firebase.firestore.ktx)
-   implementation(libs.firebase.analytics.ktx)
-   implementation(libs.firebase.crashlytics.ktx)
 
+   implementation(platform("com.google.firebase:firebase-bom:33.0.0"))
+   implementation("com.google.firebase:firebase-auth-ktx")
+   implementation("com.google.firebase:firebase-firestore-ktx")
+   implementation("com.google.firebase:firebase-analytics-ktx")
+   implementation("com.google.firebase:firebase-crashlytics-ktx")
 
    implementation("com.google.android.gms:play-services-auth:20.7.0")
}

File: gradle/libs.versions.toml

# Removed Firebase entries (not using version catalog for Firebase yet)
- [versions]
- firebase-bom = "34.6.0"
-
- [libraries]
- firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase-bom" }
- firebase-auth-ktx = { group = "com.google.firebase", name = "firebase-auth-ktx" }
- firebase-analytics-ktx = { group = "com.google.firebase", name = "firebase-analytics-ktx" }
- firebase-crashlytics-ktx = { group = "com.google.firebase", name = "firebase-crashlytics-ktx" }

Result

./gradlew clean build

Output:

BUILD SUCCESSFUL in 2m 34s
142 actionable tasks: 142 executed

SUCCESS!

Verification

# Android build
./gradlew assembleDebug assembleRelease
 Both APKs build successfully
 
# Unit tests
./gradlew test
 All tests pass
 
# iOS framework
./gradlew linkDebugFrameworkIosSimulatorArm64
 iOS framework builds

Commit: 0d2dd5aa - fix: Revert to hardcoded Firebase BOM 33.0.0


Post-Resolution: iOS Swift Type Errors

New Problem

After Android build succeeded, attempted iOS build in Xcode:

Xcode errors:

TournamentListViewModel.swift:158:97 Cannot convert value of type 'Int' to expected argument type 'Int32'
TournamentListViewModel.swift:159:97 Cannot convert value of type 'Int' to expected argument type 'Int32'
TournamentListViewModel.swift:173:94 Cannot convert value of type 'Int' to expected argument type 'Int32'

Root Cause

Kotlin’s Int type (32-bit) maps to Swift’s Int32, not Swift’s Int (platform-dependent, 64-bit on modern devices).

Solution

File: iosApp/ArcheryApprentice/ArcheryApprentice/TournamentListViewModel.swift

// Line 158
- maxScoreCorrections: sec["maxScoreCorrections"] as? Int ?? 3,
+ maxScoreCorrections: sec["maxScoreCorrections"] as? Int32 ?? 3,
 
// Line 159
- correctionTimeLimit: sec["correctionTimeLimit"] as? Int ?? 300,
+ correctionTimeLimit: sec["correctionTimeLimit"] as? Int32 ?? 300,
 
// Line 173
- dataRetentionDays: priv["dataRetentionDays"] as? Int ?? 90
+ dataRetentionDays: priv["dataRetentionDays"] as? Int32 ?? 90

Result

✅ iOS build succeeded

Commit: fe1a2403 - fix: Change Int to Int32 for Kotlin framework compatibility

Related: Kotlin-Swift Type Interoperability Guide


Post-Resolution: CI Runner Selection Issues

New Problem (Session Continuation)

Context: Self-hosted runner offline overnight, needed GitHub-hosted runner

Attempted: Pushed empty commit with [github] flag to force GitHub runner

Result: Workflow failed to detect flag

Investigation

Found 4 issues:

  1. decide_runner job hardcoded to self-hosted

    • Decision logic runs on self-hosted runner
    • If self-hosted offline, decision can’t run (chicken-egg problem)
  2. powershell vs pwsh on Linux runners

    • Windows PowerShell (powershell) doesn’t exist on Linux
    • PowerShell Core (pwsh) required for cross-platform
  3. Stale PR title in GitHub Actions event data

    • Workflow checked github.event.pull_request.title
    • Value cached at workflow start (before [github] added to title)
  4. Merge commits in PR context

    • git log -1 showed merge commit, not actual commit message
    • Commit message flags not detected

Solutions Applied

File: .github/workflows/android-ci.yml

decide_runner:
  name: Decide Runner
  runs-on: ubuntu-latest  # Changed from self-hosted (always available)
 
  steps:
    - name: Determine runner to use
      id: decide
      shell: pwsh  # Changed from powershell (works on Linux)
      run: |
        # Fetch current PR title via API (not stale event data)
        $prTitle = gh pr view ${{ github.event.pull_request.number }} --json title -q .title
 
        # Priority 1: Commit message flags
        $commitMessage = git log -1 --pretty=%B
 
        if ($commitMessage -match '\[github\]') {
          echo "runner=github" >> $env:GITHUB_OUTPUT
        } elseif ($commitMessage -match '\[self-hosted\]') {
          echo "runner=self-hosted" >> $env:GITHUB_OUTPUT
        }
        # Priority 2: PR title flags (current, not cached)
        elseif ($prTitle -match '\[github\]') {
          echo "runner=github" >> $env:GITHUB_OUTPUT
        }
        # Priority 3: Day-based default
        else {
          # ... day logic
        }

Priority System:

  1. Commit message flags → [github], [self-hosted], [skip-ci]
  2. PR title flags (fetched via API)
  3. Manual workflow_dispatch input
  4. Day-based default (days 1-25: self-hosted, 26-31: github)

Result

✅ Workflow correctly detects [github] flag and runs on GitHub-hosted runners

Commits:

  • 7f779915 - ci: Force GitHub-hosted runner + fix decide_runner to ubuntu-latest
  • dc3f0b9b - fix: Use pwsh instead of powershell for Linux runner
  • 5c6c047b - fix: Use PR title as source of truth for runner selection
  • 48c4d7e3 - fix: Fetch current PR title via API and check commit message priority

Related: GitHub Actions Dynamic Runner Selection


Session Summary

Timeline

TimeEvent
00:00Start: PR #269 Firebase BOM update
00:15Attempt #1: Fix Compose BOM → Firebase errors appear
00:30Attempt #2: Verify version catalog syntax
00:45Attempt #3: Add missing crashlytics entry → still fails
01:00Attempt #4: Fix KSP version → wrong version, red herring
01:15Attempt #5: Downgrade Firebase BOM 34.6.0 → 34.3.0 → still fails
01:30Breakthrough: Compare with main branch → BOM 33.0.0 works
01:45Research Firebase release notes → KTX deprecated in BOM 34.0.0!
02:00Attempt #6: Revert to BOM 33.0.0 hardcoded → ✅ SUCCESS
02:15iOS build: Fix Int → Int32 type conversions → ✅ iOS SUCCESS
02:30CI runner workflow fixes → ✅ Dynamic runner selection working
02:45Session complete

Commits

  1. 72bb86ce - fix: Use version catalog for Compose BOM and Firebase dependencies
  2. 9589d762 - fix: Update KSP to 2.2.21-1.0.29 (WRONG, red herring)
  3. e3fee2fd - fix: Add firebase-crashlytics-ktx to version catalog
  4. 0d2dd5aa - fix: Revert to hardcoded Firebase BOM 33.0.0 (SOLUTION)
  5. fe1a2403 - fix: Change Int to Int32 for Kotlin framework compatibility (iOS)
  6. 7f779915 - ci: Force GitHub-hosted runner + fix decide_runner to ubuntu-latest
  7. dc3f0b9b - fix: Use pwsh instead of powershell for Linux runner
  8. 5c6c047b - fix: Use PR title as source of truth for runner selection
  9. 48c4d7e3 - fix: Fetch current PR title via API and check commit message priority

Lessons Learned

1. Check External Dependency Release Notes

Lesson: When dependencies fail unexpectedly, check release notes before debugging tooling.

What Happened:

  • Assumed Gradle or version catalog issue
  • Spent 90 minutes debugging configuration
  • Real issue: Firebase deprecated libraries (external change)

Should Have Done:

  • Searched “firebase bom 34 release notes” within first 15 minutes
  • Would have found KTX deprecation immediately

2. Compare with Known-Working State Early

Lesson: Compare with working configuration as soon as unusual errors appear.

What Happened:

  • Tried 5 different fixes before comparing with main
  • Comparison immediately revealed BOM version difference

Should Have Done:

  • Compare with main branch after attempt #2 (not #5)
  • Would have saved 45 minutes

3. Version Catalog Syntax Was Never Wrong

Lesson: Don’t blame tools without evidence.

What Happened:

  • Suspected version catalog syntax issue
  • Researched Gradle docs, Stack Overflow
  • Syntax was correct all along

Reality:

  • Version catalog + BOM pattern works perfectly
  • Problem was referencing libraries that don’t exist in BOM 34.x
  • Tools worked as designed

4. Red Herrings Are Part of Debugging

Lesson: Not every investigated issue is related to the problem.

Red Herrings in This Session:

  • KSP version compatibility (unrelated)
  • Gradle cache corruption (not the cause)
  • Version catalog syntax (correct all along)
  • Network/Maven issues (not a factor)

Accept Reality:

  • Red herrings are normal in debugging
  • Document them to help future developers avoid same paths
  • Each eliminated possibility brings you closer to root cause

5. Breaking Changes Cascade

Lesson: Breaking changes in dependencies can look like configuration issues.

What Happened:

  • Firebase removed libraries from BOM (breaking change)
  • Our build failed with cryptic “empty version” error
  • Error message didn’t indicate external dependency change

Takeaway:

  • Always consider external dependency changes
  • Check release notes when upgrading
  • Major version bumps (33.x → 34.x) are red flags

Recommendations for Future PRs

1. Dependency Update Strategy

Before Updating:

  • Read release notes for major version bumps
  • Check for deprecation announcements
  • Search for “[library name] [new version] breaking changes”

During Update:

  • Update one category at a time (e.g., Firebase separate from Compose)
  • Build and test after each category
  • Commit working states incrementally

After Update:

  • Test all platforms (Android + iOS)
  • Run full test suite
  • Verify CI workflows work

2. Firebase BOM Migration Plan

When Ready to Migrate to BOM 34.x:

Step 1: Create Dedicated PR

  • Title: deps: Migrate Firebase to non-KTX modules (BOM 34.x+)
  • No other changes in same PR

Step 2: Update All Firebase Dependencies

- implementation("com.google.firebase:firebase-auth-ktx")
+ implementation("com.google.firebase:firebase-auth")

Step 3: Test Thoroughly

  • Firebase Auth flows
  • Firestore queries
  • Analytics events
  • Crashlytics reporting

Step 4: Document Migration

  • Note in PR description
  • Update README if Firebase version documented
  • Add to CHANGELOG

3. Version Catalog Adoption

Consider using version catalog for Firebase once migrated to BOM 34.x:

Benefits:

  • Centralized version management
  • Type-safe references
  • Easier future updates

Implementation:

[versions]
firebase-bom = "34.6.0"
 
[libraries]
firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase-bom" }
firebase-auth = { group = "com.google.firebase", name = "firebase-auth" }
firebase-firestore = { group = "com.google.firebase", name = "firebase-firestore" }

Technical Knowledge:

External Resources:


Conclusion

This session demonstrates that build failures aren’t always configuration issues. External dependencies change, and those changes can manifest as cryptic errors. The key debugging skills demonstrated:

  1. Systematic elimination - Tried multiple hypotheses methodically
  2. Comparison with working state - Identified BOM version difference
  3. External research - Found Firebase deprecation announcement
  4. Pragmatic decision-making - Chose low-risk solution (stay on BOM 33.0.0)
  5. Complete resolution - Fixed Android, iOS, and CI issues

Final Status: PR #269 unblocked, builds passing, ready for review


Session End: 2025-11-18 02:45 Agent: Orchestrator (Agent O) Status: Complete ✅ Week: 28