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

  1. Ensure UsbManager.hasPermission(device) is true and call usbManager.requestPermission before opening
  2. Re-enumerate the USB device list and retry with a fresh device handle
  3. Check that the security key is physically plugged in and supports CTAP HID usage pages
  4. 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

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


AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06). Data as JSON: /api/errors/faacc5c1344559cd. Report an issue: GitHub.