microg/GmsCore · error · RuntimeException

Error reading %s

Error message

Error reading %s

What it means

SafeParcelProcessor generates a Parcelable Creator whose `createFromParcel` reads fields from the Parcel inside a try/catch and wraps any exception in a RuntimeException("Error reading <class>", e). The library throws this to identify which @SafeParcelable class failed to deserialize; the `cause` holds the underlying problem (bad field format, version skew, unsupported field type, null primitive, etc.).

Source

Thrown at safe-parcel-processor/src/main/kotlin/org/microg/safeparcel/SafeParcelProcessor.kt:168

                        int end = $SafeParcelReader.readObjectHeader(parcel);
                        $fullName object;
                        try {
                            $variableDeclarations
                            $setVariablesDefault
                            while (parcel.dataPosition() < end) {
                                int header = $SafeParcelReader.readHeader(parcel);
                                int fieldId = $SafeParcelReader.getFieldId(header);
                                switch (fieldId) {
                                    $readVariablesFromParcel
                                    default:
                                        $Log.d("SafeParcel", String.format("Unknown field id %d in %s, skipping.", fieldId, "$fullName"));
                                        $SafeParcelReader.skip(parcel, header);
                                }
                            }
                            $invokeConstructor
                            $setFieldsFromVariables
                        } catch (Exception e) {
                            throw new RuntimeException(String.format("Error reading %s", "$fullName"), e);
                        }
                        if (parcel.dataPosition() > end) {
                            throw new RuntimeException(String.format("Overread allowed size end=%d", end));
                        }
                        return object;
                    }

                    @Override
                    public void writeToParcel($fullName object, $Parcel parcel, int flags) {
                        int start = $SafeParcelWriter.writeObjectHeader(parcel);
                        try {
                            $variableDeclarations
                            $setVariablesFromFields
                            $writeVariableToParcel
                        } catch (Exception e) {
                            throw new RuntimeException(String.format("Error writing %s", "$fullName"), e);
                        }
                        $SafeParcelWriter.finishObjectHeader(parcel, start);

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Inspect the cause stack trace to find the actual failing field/read in the generated reader
  2. Check whether the @SafeParcelable class schema changed (fields, @Field ids, types) since the data was written; revert the change or make reads version-tolerant
  3. Ensure the object can be constructed with the no-arg or least-args constructor and that field types are SafeParcelable-supported
  4. Regenerate/rebuild so the compiled creator matches the current class definition; confirm app and library versions align

Example fix

// before: field retype breaks old serialized data
@Field(value = 1)
var count: Int = 0
// after: keep field id/type stable, or tolerate absence via nullable read
@Field(value = 1)
var count: Int? = null
Defensive patterns

Strategy: try-catch

Validate before calling

// verify parcel has payload before reading
if (parcel == null || parcel.dataAvail() <= 0) return null

Type guard

fun <T : Parcelable?> safeUnparcel(parcel: Parcel?, creator: Parcelable.Creator<T>): T? =
  if (parcel != null && parcel.dataAvail() > 0) try { creator.createFromParcel(parcel) } catch (e: RuntimeException) { null } else null

Try / catch

try {
  val obj = CREATOR.createFromParcel(parcel)
} catch (e: RuntimeException) {
  if (e.message?.startsWith("Error reading") == true) {
    Log.w(TAG, "SafeParcel read failed", e.cause)
    obj = null // rebuild default / re-persist fresh data
  } else throw e
}

Prevention

When it happens

Trigger: Parcel.readFromParcel-generated code for a @SafeParcelable class throws any Exception during field reading: a readVariable returns corrupt/mismatched data, the serialized blob was written by an incompatible version of the class (field added/removed/retyped), or the parcel data is truncated/malformed.

Common situations: App updated a @SafeParcelable class (changed field order/type or removed a field without bumping the class version) while old serialized bytes (e.g. persisted Intents, AccountManager data, Play Services IPC payloads) are still being unparceled; mixing app and library versions that disagree on the parcel schema; passing a Parcel positioned at the wrong offset.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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