microg/GmsCore · error · IllegalArgumentException
Executor must not be null
Error message
Executor must not be null
What it means
The deprecated Tasks.call(Executor, Callable) validates its arguments up front. A null Executor cannot schedule the Callable, so the method throws this IllegalArgumentException immediately instead of failing later inside executor.execute().
Source
Thrown at play-services-tasks/src/main/java/com/google/android/gms/tasks/Tasks.java:115
* @deprecated Use {@link TaskCompletionSource} instead, which allows the caller to manage their own Executor.
*/
@Deprecated
public static <TResult> Task<TResult> call(Callable<TResult> callable) {
return call(TaskExecutors.MAIN_THREAD, callable);
}
/**
* Returns a {@link Task} that will be completed with the result of the specified {@code Callable}.
* <p/>
* If a non-{@link Exception} throwable is thrown in the callable, the {@link Task} will be failed with a
* {@link RuntimeException} whose cause is the original throwable.
*
* @param executor the Executor to use to call the {@code Callable}
* @deprecated Use {@link TaskCompletionSource} instead, which allows the caller to manage their own Executor.
*/
@Deprecated
public static <TResult> Task<TResult> call(Executor executor, Callable<TResult> callable) {
if (executor == null) throw new IllegalArgumentException("Executor must not be null");
if (callable == null) throw new IllegalArgumentException("Callable must not be null");
TaskCompletionSource<TResult> taskCompletionSource = new TaskCompletionSource<>();
executor.execute(() -> {
try {
taskCompletionSource.setResult(callable.call());
} catch (Exception e) {
taskCompletionSource.trySetException(e);
} catch (Throwable t) {
taskCompletionSource.trySetException(new RuntimeException(t));
}
});
return taskCompletionSource.getTask();
}
/**
* Returns a canceled Task.
*/
public static <TResult> Task<TResult> forCancelled() {View on GitHub (pinned to 157c9d86ac)
Solutions
- Pass a real Executor, e.g. Executors.newSingleThreadExecutor(), or TaskExecutors/ContextCompat.getMainExecutor(context)
- Keep using the no-executor overload Tasks.call(Callable) only if you intend the deprecated default behavior — better: migrate to TaskCompletionSource as the Javadoc recommends
- Audit where the Executor value originates and ensure it is initialized before Tasks.call is invoked
Example fix
// before
Task<String> task = Tasks.call(null, () -> loadData()); // throws
// after
Executor executor = Executors.newSingleThreadExecutor();
Task<String> task = Tasks.call(executor, () -> loadData());
// or preferred modern form:
TaskCompletionSource<String> tcs = new TaskCompletionSource<>();
executor.execute(() -> { try { tcs.setResult(loadData()); } catch (Exception e) { tcs.setException(e); } }); Defensive patterns
Strategy: validation
Validate before calling
if (executor == null) {
executor = Executors.newSingleThreadExecutor();
} Type guard
static boolean isRunnableExecutor(Executor e) {
return e != null;
} Try / catch
try {
Task<T> t = Tasks.call(executor, callable);
} catch (IllegalArgumentException e) {
// fall back to a default executor
Task<T> t = Tasks.call(Executors.newSingleThreadExecutor(), callable);
} Prevention
- Standardize on one Executor provider (DI binding or utility) so it is never null
- Prefer Tasks.call(Callable) or TaskCompletionSource over the deprecated executor overload
- Initialize executors in Application/Component creation, not lazily at call sites
When it happens
Trigger: Calling Tasks.call(null, callable), usually because the Executor came from a variable/config that is null (e.g. a field never initialized, an injected executor not provided, or doInBackground-style code passing null to use 'default' behavior).
Common situations: Migrating code from Tasks.call(Callable) to the executor variant and passing null hoping for a default executor; dependency-injection graph missing an Executor binding; custom Executor created conditionally (e.g. after a permission/setup step) but call() runs earlier.
Related errors
- Callable must not be null
- Conflicting data sources found
- durationMillis must be greater than 0
- illegal fastest interval:
- intervalMillis must be greater than or equal to 0
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/57fbd8df03cff2fb.
Report an issue: GitHub.