microg/GmsCore · error · IOException
(String) res
Error message
(String) res
What it means
In sendRegisterMessageBlockingInternal, when the blocking response for a requestId is a plain String instead of an Intent, the library treats that string as an error message and rethrows it inside an IOException. The message here is the raw error string sent back by the registration service, e.g. ERROR_SERVICE_NOT_AVAILABLE or a service-specific code.
Source
Thrown at play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdRpc.java:387
return intent;
}
private Intent sendRegisterMessageBlockingInternal(Bundle data, KeyPair keyPair) throws IOException {
ConditionVariable cv = new ConditionVariable();
String requestId = getRequestId();
synchronized (InstanceIdRpc.class) {
blockingResponses.put(requestId, cv);
}
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()) {View on GitHub (pinned to 157c9d86ac)
Solutions
- Catch the IOException and inspect getMessage() to branch on known codes (ERROR_SERVICE_NOT_AVAILABLE, ERROR_BACKOFF, etc.)
- Retry with exponential backoff when the message is ERROR_SERVICE_NOT_AVAILABLE
- Check network connectivity and Google Play Services availability before calling getToken
- Log the raw message — a non-standard string often indicates a provider-side (GMS/microG) problem
Example fix
// before
String token = instanceId.getToken(senderId, "GCM");
// after
try {
String token = instanceId.getToken(senderId, "GCM");
} catch (IOException e) {
if ("ERROR_SERVICE_NOT_AVAILABLE".equals(e.getMessage())) {
scheduleRetryWithBackoff();
} else {
Log.w(TAG, "Registration failed: " + e.getMessage());
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure connectivity before registration if (!isNetworkAvailable(context)) deferRegistration();
Try / catch
try {
String token = instanceId.getToken(senderId, "GCM");
} catch (IOException e) {
String msg = e.getMessage();
if (msg != null && msg.startsWith("ERROR_")) {
handleKnownIidError(msg); // branch per known code, retry if transient
} else {
logAndReport(msg); // provider-specific error
}
} Prevention
- Log e.getMessage() verbatim to identify provider-side codes
- Retry transient codes with backoff; surface config codes to the developer
- Keep Play Services/microG updated
When it happens
Trigger: The IID service (or microG's RPC receiver) delivered an error result via setResponse(requestId, errorMessage) for the pending request; the blocked thread wakes with a String payload and throws IOException(errorMessage).
Common situations: Server-side registration failures (quota, invalid sender); device connectivity problems mid-registration; Play Services returning structured error strings instead of result Intents; microG implementations mapping GMS errors to string responses.
Related errors
- No acceptable module " + moduleId + " found. Local version i
- ERROR_BACKOFF
- ERROR_MISSING_INSTANCEID_SERVICE
- ERROR_TIMEOUT
- ERROR_SERVICE_NOT_AVAILABLE
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/51422e6b25504230.
Report an issue: GitHub.