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
- Inspect the cause stack trace to find the actual failing field/read in the generated reader
- Check whether the @SafeParcelable class schema changed (fields, @Field ids, types) since the data was written; revert the change or make reads version-tolerant
- Ensure the object can be constructed with the no-arg or least-args constructor and that field types are SafeParcelable-supported
- 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
- Never change @Field ids or types of a SafeParcelable class once data has been persisted; only append new fields
- Keep app and microG/library versions in sync across processes
- Wrap all unparceling of persisted or IPC data in try-catch with a fallback default instance
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
- Error writing %s
- null reference
- Could not read from parcel file descriptor
- ParcelableKeyValue.key must be > 0
- ParcelableKeyValue.value must not be null
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/e26d7290dfa2dfbb.
Report an issue: GitHub.