{"record":{"id":"99624c6c3cd43ffd","repo":"microg/GmsCore","slug":"error-timeout","errorCode":"ERROR_TIMEOUT","errorMessage":"ERROR_TIMEOUT","messagePattern":"ERROR_TIMEOUT","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdRpc.java","lineNumber":390,"sourceCode":"    private Intent sendRegisterMessageBlockingInternal(Bundle data, KeyPair keyPair) throws IOException {\n        ConditionVariable cv = new ConditionVariable();\n        String requestId = getRequestId();\n        synchronized (InstanceIdRpc.class) {\n            blockingResponses.put(requestId, cv);\n        }\n\n        sendRegisterMessage(data, keyPair, requestId);\n\n        cv.block(BLOCKING_WAIT_TIME);\n        synchronized (InstanceIdRpc.class) {\n            Object res = blockingResponses.remove(requestId);\n            if (res instanceof Intent) {\n                return (Intent) res;\n            } else if (res instanceof String) {\n                throw new IOException((String) res);\n            }\n            Log.w(TAG, \"No response \" + res);\n            throw new IOException(ERROR_TIMEOUT);\n        }\n    }\n\n    public String handleRegisterMessageResult(Intent resultIntent) throws IOException {\n        if (resultIntent == null) throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);\n        String result = resultIntent.getStringExtra(EXTRA_REGISTRATION_ID);\n        if (result == null) result = resultIntent.getStringExtra(EXTRA_UNREGISTERED);\n        if (result != null) return result;\n        result = resultIntent.getStringExtra(EXTRA_ERROR);\n        throw new IOException(result != null ? result : ERROR_SERVICE_NOT_AVAILABLE);\n    }\n\n    private void setResponse(String requestId, Object response) {\n        if (requestId == null) {\n            for (String r : blockingResponses.keySet()) {\n                setResponse(r, response);\n            }\n        }","sourceCodeStart":372,"sourceCodeEnd":408,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdRpc.java#L372-L408","documentation":"sendRegisterMessageBlockingInternal throws ERROR_TIMEOUT when, after BLOCKING_WAIT_TIME elapses, no response (Intent or error String) was recorded for the request in blockingResponses. The registration RPC never completed in time, so the pending waiter gives up with this IOException.","triggerScenarios":"Calling getToken while the device is offline or the Play Services registration service is unresponsive; the reply broadcast to the C2DM_REGISTER intent is lost or delayed beyond BLOCKING_WAIT_TIME; the target service process crashes before answering.","commonSituations":"Poor or flaky mobile connectivity during app startup token fetch; devices where Play Services is busy or just updated; emulators without network access; microG setups whose push connection is not yet established.","solutions":["Retry the token request after a delay with exponential backoff, ideally when connectivity is confirmed available","Use a ConnectivityManager NetworkCallback to wait for an active network before calling getToken","Check that Google Play Services is running and up to date; restart the device if the service is wedged","Increase resilience by registering tokens in a background job (WorkManager/JobScheduler) rather than blocking user-facing code"],"exampleFix":"// before\nString token = instanceId.getToken(senderId, \"GCM\");\n// after\nretryWithBackoff(5, 500, () -> {\n    try { return instanceId.getToken(senderId, \"GCM\"); }\n    catch (IOException e) {\n        if (\"ERROR_TIMEOUT\".equals(e.getMessage())) return null; // retry\n        throw e;\n    }\n});","handlingStrategy":"retry","validationCode":"NetworkInfo net = cm.getActiveNetworkInfo();\nif (net == null || !net.isConnected()) deferRegistrationUntilOnline();","typeGuard":null,"tryCatchPattern":"try {\n    String token = instanceId.getToken(senderId, \"GCM\");\n} catch (IOException e) {\n    if (\"ERROR_TIMEOUT\".equals(e.getMessage())) {\n        enqueueRetryWithBackoff(); // e.g. WorkManager\n    }\n}","preventionTips":["Register tokens from a background job with retry policy, not synchronously at startup","Wait for network availability before calling getToken","Avoid blocking user-facing flows on registration RPCs"],"tags":["android","timeout","registration","networking"],"backgroundTag":"request-timeout","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"}