microg/GmsCore · error · RuntimeException
Could not open device
Error message
Could not open device
What it means
CtapHidConnection.open() wraps the suspend block `open(block)` which first calls the boolean `open()`; if the device failed to open, it throws RuntimeException("Could not open device"). This means the underlying USB HID device could not be claimed/connected at the CTAP-HID transport level.
Source
Thrown at play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/usb/ctaphid/CtapHidConnection.kt:177
}
throw RuntimeException("Unexpected response: $response")
}
override suspend fun <Q: Ctap2Request, S: Ctap2Response> runCommand(command: Ctap2Command<Q, S>): S {
require(hasCtap2Support)
sendRequest(CtapHidCborRequest(command.request))
val response = readResponse(command.timeout)
if (response is CtapHidCborResponse) {
if (response.statusCode == 0x00.toByte()) {
return command.decodeResponse(response.payload)
}
throw Ctap2StatusException(response.statusCode)
}
throw RuntimeException("Unexpected response: $response")
}
suspend fun <R> open(block: suspend (CtapHidConnection) -> R): R {
if (!open()) throw RuntimeException("Could not open device")
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
when (exception) {
null -> close()
else -> try {
close()
} catch (closeException: Throwable) {
// cause.addSuppressed(closeException) // ignored here
}
}
}
}
View on GitHub (pinned to 157c9d86ac)
Solutions
- Ensure UsbManager.hasPermission(device) is true and call usbManager.requestPermission before opening
- Re-enumerate the USB device list and retry with a fresh device handle
- Check that the security key is physically plugged in and supports CTAP HID usage pages
- Retry the open after a short delay, since hotplug race conditions commonly cause transient failures
Example fix
// before
ctapConnection.open { conn -> conn.reset() }
// after
if (!usbManager.hasPermission(usbDevice)) {
requestUsbPermission(usbDevice) // then retry open in the permission callback
}
ctapConnection.open { conn -> conn.reset() } Defensive patterns
Strategy: try-catch
Validate before calling
val hasPermission = usbManager.hasPermission(usbDevice) val isOpenable = usbDevice != null && hasPermission
Type guard
fun UsbDevice?.isClaimable(usbManager: UsbManager): Boolean =
this != null && usbManager.hasPermission(this) Try / catch
try {
ctapConnection.open { conn -> /* use conn */ }
} catch (e: RuntimeException) {
if (e.message == "Could not open device") promptUserToReplugKey()
else throw e
} Prevention
- Request USB permission via UsbManager.requestPermission before opening
- Re-enumerate devices on ACTION_USB_DEVICE_ATTACHED/DETACHED broadcasts
- Keep the security key plugged in for the whole ceremony
- Retry open with backoff after hotplug events
When it happens
Trigger: Calling open { ... } on a CtapHidConnection when open() returns false — e.g. the USB device is not present, was unplugged, is claimed by another process, or lacks permission (android.permission / UsbManager requestPermission not granted).
Common situations: User unplugs the security key mid-flow; app never requested USB permission for the device; the FIDO USB authenticator is held by the browser/another app; device descriptor mismatch after hotplug.
Understand the failure class
Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.
Related errors
- Transport ${transport} not supported
- Attachment ${attachment} not supported
- Attestation conveyance preference ${attachment} not supporte
- No response set.
- PublicKeyCredentialType ${type} not supported
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/faacc5c1344559cd.
Report an issue: GitHub.