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

  1. Retry registration with exponential backoff — this error is usually transient
  2. Verify device connectivity before retrying (register a ConnectivityManager callback)
  3. Ensure Google Play Services is installed, enabled, and up to date
  4. 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

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


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