microg/GmsCore · error · IllegalArgumentException

No non-synthetic fields were found

Error message

No non-synthetic fields were found

What it means

After skipping synthetic (instrumentation-added, e.g. JaCoCo $jacocoData) fields, ObjectWrapper.unwrap requires exactly one real field to hold the wrapped object. If none remains, it throws IllegalArgumentException('No non-synthetic fields were found').

Source

Thrown at play-services-basement/src/main/java/com/google/android/gms/dynamic/ObjectWrapper.java:68

        // https://www.jacoco.org/jacoco/trunk/doc/faq.html

        @Nullable
        Field field = null;

        for (Field currentField : fields) {
            if (currentField.isSynthetic()) {
                continue;
            }

            if (field == null) {
                field = currentField;
            } else {
                throw new IllegalArgumentException("Too many non-synthetic fields were found");
            }
        }

        if (field == null) {
            throw new IllegalArgumentException("No non-synthetic fields were found");
        }

        if (!field.isAccessible()) {
            field.setAccessible(true);
            try {
                return field.get(binder);
            } catch (NullPointerException localNullPointerException) {
                throw new IllegalArgumentException("Binder object is null.",
                        localNullPointerException);
            } catch (IllegalArgumentException localIllegalArgumentException) {
                throw new IllegalArgumentException("remoteBinder is the wrong class.",
                        localIllegalArgumentException);
            } catch (IllegalAccessException localIllegalAccessException) {
                throw new IllegalArgumentException("Could not access the field in remoteBinder.",
                        localIllegalAccessException);
            }
        } else {
            throw new IllegalArgumentException();

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Exclude com.google.android.gms.** from JaCoCo/proguard/R8 instrumentation and shrinking (keepclassmembers for the wrapper field)
  2. Ensure the binder is a genuine ObjectWrapper remote binder created by ObjectWrapper.wrap
  3. Verify no test/instrumentation agent is transforming the class at runtime
  4. Pass the wrapped object directly if you have it, avoiding the binder round-trip

Example fix

// before
Object v = ObjectWrapper.unwrap(binder); // fails under JaCoCo
// after
if (binder instanceof ObjectWrapper) {
    Object v = ObjectWrapper.unwrapTyped(binder, Object.class);
} else {
    Object v = binder; // already local
}
Defensive patterns

Strategy: try-catch

Validate before calling

IBinder b = wrapper.asBinder();
boolean hasReal = false;
for (Field f : b.getClass().getDeclaredFields()) {
    if (!f.isSynthetic() && !f.getName().startsWith("$jacoco")) { hasReal = true; break; }
}
if (!hasReal) { /* reject: only synthetic fields */ }

Try / catch

try {
    T value = ObjectWrapper.unwrapTyped(wrapper, T.class);
} catch (IllegalArgumentException e) {
    // JaCoCo/proguard stripped the field — use local object instead
}

Prevention

When it happens

Trigger: unwrap() called on a binder class whose only declared fields are synthetic — commonly a JaCoCo-instrumented build where $jacocoData is filtered out, an empty mock/proxy subclass, or a wrapper class whose payload field was stripped by proguard/R8.

Common situations: Running code under JaCoCo coverage where the remote binder class got instrumented; obfuscation removing the wrapper's field; mocks standing in for IObjectWrapper in production paths.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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