microg/GmsCore · error · RuntimeException
Collision in hash string, can't use SMS Retriever API
Error message
Collision in hash string, can't use SMS Retriever API
What it means
startSmsRetriever throws this when anyOtherPackageHasHashString(packageName, appHashString) returns true: another installed package computes the same 11-character SMS Retriever hash (derived from package name + signing certificate SHA-256). Because OTP codes are broadcast to whichever app holds the matching hash, a hash collision would let another app intercept your SMS codes, so the library refuses to start.
Source
Thrown at play-services-auth-api-phone/core/src/main/kotlin/org/microg/gms/auth/phone/SmsRetrieverCore.kt:101
context.registerReceiver(smsBroadcastReceiver, intentFilter)
}
}
}
private suspend fun ensureReady(permissions: Array<String>): Boolean {
if (SDK_INT < 19) throw RuntimeException("Version not supported")
if (!ensurePermission(permissions)) return false
configureBroadcastListenersIfNeeded()
return true
}
suspend fun startSmsRetriever(packageName: String) {
val appHashString = getHashString(packageName)
if (!ensureReady(arrayOf(RECEIVE_SMS)))
throw RuntimeException("Initialization failed")
if (anyOtherPackageHasHashString(packageName, appHashString))
throw RuntimeException("Collision in hash string, can't use SMS Retriever API")
if (requests.values.any { it.packageName == packageName && it.appHashString == appHashString && it.type == RETRIEVER })
throw RuntimeException("App already listening")
val request = SmsRetrieverRequest(
id = requestIdCounter.incrementAndGet(),
type = RETRIEVER,
packageName = packageName,
appHashString = appHashString,
timeoutPendingIntent = getTimeoutPendingIntent(context, packageName)
)
requests[request.id] = request
alarmManager.set(AlarmManager.RTC, request.creation + TIMEOUT, request.timeoutPendingIntent)
}
suspend fun startWithConsentPrompt(packageName: String, senderPhoneNumber: String?) {
if (!ensureReady(arrayOf(RECEIVE_SMS, READ_CONTACTS)))
throw RuntimeException("Initialization failed")
if (requests.values.any { it.packageName == packageName && it.senderPhoneNumber == senderPhoneNumber && it.type == USER_CONSENT })View on GitHub (pinned to 157c9d86ac)
Solutions
- Uninstall the conflicting app that shares the same hash string (check other installed builds of your app)
- Sign your app with a unique release keystore so its hash differs from other packages
- Rename the applicationId of duplicate test builds to avoid hash overlap
- Remove clone/parallel-app wrappers that re-sign packages with a shared key
Defensive patterns
Strategy: try-catch
Try / catch
try { core.startSmsRetriever(pkg) } catch (e: RuntimeException) { if (e.message?.startsWith("Collision in hash string") == true) showHashConflictUi() else throw e } Prevention
- Sign release and debug builds with distinct keys
- Avoid installing clone/duplicate builds of the same app on test devices
- Use unique applicationIds per build variant
When it happens
Trigger: Two installed apps signed with the same certificate and sharing the same package-name/hash derivation, or a test/debug-signed duplicate build of your app installed alongside the real app.
Common situations: Debug and release builds signed with the same debug key installed side by side; clone/parallel-space apps; re-signed APKs installed on the same device during development.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Version not supported
- Initialization failed
- App already listening
- No signature found for $packageName
- deleteAll was set to true but keys were also provided
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/c01f1d8ccd047880.
Report an issue: GitHub.