{"record":{"id":"faacc5c1344559cd","repo":"microg/GmsCore","slug":"could-not-open-device","errorCode":null,"errorMessage":"Could not open device","messagePattern":"Could not open device","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/usb/ctaphid/CtapHidConnection.kt","lineNumber":177,"sourceCode":"        }\n        throw RuntimeException(\"Unexpected response: $response\")\n    }\n\n    override suspend fun <Q: Ctap2Request, S: Ctap2Response> runCommand(command: Ctap2Command<Q, S>): S {\n        require(hasCtap2Support)\n        sendRequest(CtapHidCborRequest(command.request))\n        val response = readResponse(command.timeout)\n        if (response is CtapHidCborResponse) {\n            if (response.statusCode == 0x00.toByte()) {\n                return command.decodeResponse(response.payload)\n            }\n            throw Ctap2StatusException(response.statusCode)\n        }\n        throw RuntimeException(\"Unexpected response: $response\")\n    }\n\n    suspend fun <R> open(block: suspend (CtapHidConnection) -> R): R {\n        if (!open()) throw RuntimeException(\"Could not open device\")\n        var exception: Throwable? = null\n        try {\n            return block(this)\n        } catch (e: Throwable) {\n            exception = e\n            throw e\n        } finally {\n            when (exception) {\n                null -> close()\n                else -> try {\n                    close()\n                } catch (closeException: Throwable) {\n                    // cause.addSuppressed(closeException) // ignored here\n                }\n            }\n        }\n    }\n","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/usb/ctaphid/CtapHidConnection.kt#L159-L195","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nctapConnection.open { conn -> conn.reset() }\n// after\nif (!usbManager.hasPermission(usbDevice)) {\n    requestUsbPermission(usbDevice) // then retry open in the permission callback\n}\nctapConnection.open { conn -> conn.reset() }","handlingStrategy":"try-catch","validationCode":"val hasPermission = usbManager.hasPermission(usbDevice)\nval isOpenable = usbDevice != null && hasPermission","typeGuard":"fun UsbDevice?.isClaimable(usbManager: UsbManager): Boolean =\n    this != null && usbManager.hasPermission(this)","tryCatchPattern":"try {\n    ctapConnection.open { conn -> /* use conn */ }\n} catch (e: RuntimeException) {\n    if (e.message == \"Could not open device\") promptUserToReplugKey()\n    else throw e\n}","preventionTips":["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"],"tags":["usb","fido","device-open","android"],"backgroundTag":"file-open-failed","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}