{"record":{"id":"e26d7290dfa2dfbb","repo":"microg/GmsCore","slug":"error-reading-s","errorCode":null,"errorMessage":"Error reading %s","messagePattern":"Error reading (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"safe-parcel-processor/src/main/kotlin/org/microg/safeparcel/SafeParcelProcessor.kt","lineNumber":168,"sourceCode":"                        int end = $SafeParcelReader.readObjectHeader(parcel);\n                        $fullName object;\n                        try {\n                            $variableDeclarations\n                            $setVariablesDefault\n                            while (parcel.dataPosition() < end) {\n                                int header = $SafeParcelReader.readHeader(parcel);\n                                int fieldId = $SafeParcelReader.getFieldId(header);\n                                switch (fieldId) {\n                                    $readVariablesFromParcel\n                                    default:\n                                        $Log.d(\"SafeParcel\", String.format(\"Unknown field id %d in %s, skipping.\", fieldId, \"$fullName\"));\n                                        $SafeParcelReader.skip(parcel, header);\n                                }\n                            }\n                            $invokeConstructor\n                            $setFieldsFromVariables\n                        } catch (Exception e) {\n                            throw new RuntimeException(String.format(\"Error reading %s\", \"$fullName\"), e);\n                        }\n                        if (parcel.dataPosition() > end) {\n                            throw new RuntimeException(String.format(\"Overread allowed size end=%d\", end));\n                        }\n                        return object;\n                    }\n\n                    @Override\n                    public void writeToParcel($fullName object, $Parcel parcel, int flags) {\n                        int start = $SafeParcelWriter.writeObjectHeader(parcel);\n                        try {\n                            $variableDeclarations\n                            $setVariablesFromFields\n                            $writeVariableToParcel\n                        } catch (Exception e) {\n                            throw new RuntimeException(String.format(\"Error writing %s\", \"$fullName\"), e);\n                        }\n                        $SafeParcelWriter.finishObjectHeader(parcel, start);","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/safe-parcel-processor/src/main/kotlin/org/microg/safeparcel/SafeParcelProcessor.kt#L150-L186","documentation":"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.).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: field retype breaks old serialized data\n@Field(value = 1)\nvar count: Int = 0\n// after: keep field id/type stable, or tolerate absence via nullable read\n@Field(value = 1)\nvar count: Int? = null","handlingStrategy":"try-catch","validationCode":"// verify parcel has payload before reading\nif (parcel == null || parcel.dataAvail() <= 0) return null","typeGuard":"fun <T : Parcelable?> safeUnparcel(parcel: Parcel?, creator: Parcelable.Creator<T>): T? =\n  if (parcel != null && parcel.dataAvail() > 0) try { creator.createFromParcel(parcel) } catch (e: RuntimeException) { null } else null","tryCatchPattern":"try {\n  val obj = CREATOR.createFromParcel(parcel)\n} catch (e: RuntimeException) {\n  if (e.message?.startsWith(\"Error reading\") == true) {\n    Log.w(TAG, \"SafeParcel read failed\", e.cause)\n    obj = null // rebuild default / re-persist fresh data\n  } else throw e\n}","preventionTips":["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"],"tags":["android","parcel","serialization","runtime-exception"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}