microg/GmsCore · error · IllegalStateException
looper is null and the calling thread has not called Looper.
Error message
looper is null and the calling thread has not called Looper.prepare()
What it means
microG's FusedLocationProviderClientImpl throws this IllegalStateException when the Looper used to deliver location updates cannot be resolved. If the caller passes a null Looper, the library falls back to Looper.myLooper(); on a thread that never called Looper.prepare() there is no per-thread Looper, so delivery of updates would be impossible and the library fails fast.
Source
Thrown at play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderClientImpl.java:81
}
@NonNull
@Override
public Task<Void> removeLocationUpdates(@NonNull LocationListener listener) {
return scheduleTask((VoidReturningGoogleApiCall<LocationClientImpl>) (client) -> client.removeLocationUpdates(listener));
}
@NonNull
@Override
public Task<Void> removeLocationUpdates(@NonNull PendingIntent pendingIntent) {
return scheduleTask((VoidReturningGoogleApiCall<LocationClientImpl>) (client) -> client.removeLocationUpdates(pendingIntent));
}
@NonNull
@Override
public Task<Void> requestLocationUpdates(@NonNull LocationRequest request, @NonNull LocationListener listener, @Nullable Looper looper) {
Looper currentLooper = looper == null ? Looper.myLooper() : looper;
if (currentLooper == null) throw new IllegalStateException("looper is null and the calling thread has not called Looper.prepare()");
return scheduleTask((VoidReturningGoogleApiCall<LocationClientImpl>) (client) -> client.requestLocationUpdates(request, listener, currentLooper));
}
@NonNull
@Override
public Task<Void> requestLocationUpdates(@NonNull LocationRequest request, @NonNull Executor executor, @NonNull LocationCallback callback) {
return scheduleTask((VoidReturningGoogleApiCall<LocationClientImpl>) (client) -> client.requestLocationUpdates(request, executor, callback));
}
@NonNull
@Override
public Task<Void> requestLocationUpdates(@NonNull LocationRequest request, @NonNull Executor executor, @NonNull LocationListener listener) {
return scheduleTask((VoidReturningGoogleApiCall<LocationClientImpl>) (client) -> client.requestLocationUpdates(request, executor, listener));
}
@NonNull
@Override
public Task<Void> requestLocationUpdates(@NonNull LocationRequest request, @NonNull LocationCallback callback, Looper looper) {View on GitHub (pinned to 157c9d86ac)
Solutions
- Pass Looper.getMainLooper() explicitly so updates are delivered on the main thread
- Call Looper.prepare() on the background thread before requesting updates, or use a HandlerThread and pass its Looper
- Use the Executor-based overload requestLocationUpdates(request, executor, callback) which does not require a Looper
Example fix
// before
thread {
client.requestLocationUpdates(request, callback, null)
}
// after
thread {
client.requestLocationUpdates(request, callback, Looper.getMainLooper())
} Defensive patterns
Strategy: validation
Validate before calling
Looper looper = looperArg != null ? looperArg : Looper.myLooper(); if (looper == null) looper = Looper.getMainLooper(); client.requestLocationUpdates(request, listener, looper);
Type guard
static boolean hasLooper() { return Looper.myLooper() != null; } Prevention
- Always pass an explicit Looper (usually Looper.getMainLooper()) to requestLocationUpdates
- Prefer the Executor-based overload on threads without a Looper
- Never assume the calling thread has a Looper; background/Executor threads do not
When it happens
Trigger: Calling requestLocationUpdates(LocationRequest, LocationListener) or requestLocationUpdates(LocationRequest, LocationCallback) with a null Looper from a thread that has not called Looper.prepare() (e.g. a plain background Thread, Executor thread, or Kotlin coroutine on Dispatchers.Default).
Common situations: Requesting location updates from a worker thread without an associated Looper; passing an uninitialized Looper variable; migrating from Google Play services where a null Looper was tolerated by using the main looper implicitly.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- invalid radius:
- invalid latitude:
- invalid longitude:
- maxUpdateAgeMillis must be greater than 0
- durationMillis must be greater than 0
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/ed4c4fb952a6c185.
Report an issue: GitHub.