microg/GmsCore · warning · IOException
ERROR_SERVICE_NOT_AVAILABLE
ERROR_SERVICE_NOT_AVAILABLE
Error message
ERROR_SERVICE_NOT_AVAILABLE
What it means
handleRegisterMessageResult throws ERROR_SERVICE_NOT_AVAILABLE when the registration result Intent is null — the service never delivered a usable reply. This signals the registration endpoint/service could not be reached or failed to respond with a result.
Source
Thrown at play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdRpc.java:395
}
sendRegisterMessage(data, keyPair, requestId);
cv.block(BLOCKING_WAIT_TIME);
synchronized (InstanceIdRpc.class) {
Object res = blockingResponses.remove(requestId);
if (res instanceof Intent) {
return (Intent) res;
} else if (res instanceof String) {
throw new IOException((String) res);
}
Log.w(TAG, "No response " + res);
throw new IOException(ERROR_TIMEOUT);
}
}
public String handleRegisterMessageResult(Intent resultIntent) throws IOException {
if (resultIntent == null) throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
String result = resultIntent.getStringExtra(EXTRA_REGISTRATION_ID);
if (result == null) result = resultIntent.getStringExtra(EXTRA_UNREGISTERED);
if (result != null) return result;
result = resultIntent.getStringExtra(EXTRA_ERROR);
throw new IOException(result != null ? result : ERROR_SERVICE_NOT_AVAILABLE);
}
private void setResponse(String requestId, Object response) {
if (requestId == null) {
for (String r : blockingResponses.keySet()) {
setResponse(r, response);
}
}
Object old = blockingResponses.get(requestId);
blockingResponses.put(requestId, response);
if (old instanceof ConditionVariable) {
((ConditionVariable) old).open();
} else if (old instanceof Messenger) {View on GitHub (pinned to 157c9d86ac)
Solutions
- Retry registration with exponential backoff — this error is usually transient
- Verify device connectivity before retrying (register a ConnectivityManager callback)
- Ensure Google Play Services is installed, enabled, and up to date
- Fall back to deferred registration (WorkManager retry policy) so tokens are fetched once connectivity returns
Example fix
// before
String token = rpc.handleRegisterMessageResult(intent);
// after
try {
String token = rpc.handleRegisterMessageResult(intent);
} catch (IOException e) {
if ("ERROR_SERVICE_NOT_AVAILABLE".equals(e.getMessage())) {
workManagerEnqueueRetry(); // retry with backoff when online
}
} Defensive patterns
Strategy: retry
Validate before calling
if (resultIntent == null) scheduleRetryWithBackoff(); // mimic library check upstream
Try / catch
try {
String token = rpc.handleRegisterMessageResult(intent);
} catch (IOException e) {
if ("ERROR_SERVICE_NOT_AVAILABLE".equals(e.getMessage())) {
retryWithExponentialBackoff();
}
} Prevention
- Treat this as transient: implement idempotent retry with backoff
- Defer registration until the device has connectivity
- Register tokens in WorkManager jobs that survive process death
When it happens
Trigger: handleRegisterMessageResult is invoked with a null resultIntent, typically from the broadcast/onHandleIntent path that receives the C2DM register reply when the reply Intent was never received (device offline, service unavailable, GMS hiccup).
Common situations: GCM/FCM registration during network outages; devices in Doze or with radios off; Play Services crash or update in progress; long-standing GCM issue where broadcasts are silently dropped.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- ERROR_BACKOFF
- (String) res
- ERROR_TIMEOUT
- deleteAll was set to true but keys were also provided
- Element in keys cannot be null or empty
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/a7268ece1506ce62.
Report an issue: GitHub.