{"record":{"id":"6ee065ba2498465d","repo":"grpc/grpc-java","slug":"can-t-unmarshall-a-parcelable-from-a-regular-byte","errorCode":null,"errorMessage":"Can't unmarshall a parcelable from a regular byte stream","messagePattern":"Can't unmarshall a parcelable from a regular byte stream","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"binder/src/main/java/io/grpc/binder/internal/MetadataHelper.java","lineNumber":223,"sourceCode":"\n    public ParcelableMetadataMarshaller(\n        @Nullable Parcelable.Creator<P> creator, boolean immutableType) {\n      this.creator = creator;\n      this.immutableType = immutableType;\n    }\n\n    @Override\n    public InputStream toStream(P value) {\n      return new ParcelableInputStream<>(creator, value, immutableType);\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public P parseStream(InputStream stream) {\n      if (stream instanceof ParcelableInputStream) {\n        return ((ParcelableInputStream<P>) stream).getParcelable();\n      } else {\n        throw new UnsupportedOperationException(\n            \"Can't unmarshall a parcelable from a regular byte stream\");\n      }\n    }\n  }\n}\n","sourceCodeStart":205,"sourceCodeEnd":229,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/binder/src/main/java/io/grpc/binder/internal/MetadataHelper.java#L205-L229","documentation":"The Parcelable marshaller can only deserialize values that were round-tripped through Android Parcel-backed streams (ParcelableInputStream), which carry the raw Parcelable object. parseStream called on a plain InputStream has no way to recover the Parcelable from raw bytes, so it throws UnsupportedOperationException.","triggerScenarios":"Calling parseStream() of the Parcelable marshaller with any InputStream that is not a ParcelableInputStream - e.g. feeding it bytes read over the network, a file, or a BufferedInputStream wrapper instead of the original parcel-backed stream.","commonSituations":"Bridging gRPC messages into non-binder transports; wrapping the transport stream in buffering/decoding streams that lose the ParcelableInputStream type; unit tests that substitute ByteArrayInputStream for the Android stream.","solutions":["Ensure the marshaller's parseStream is only invoked with the ParcelableInputStream produced by the binder transport; do not wrap or copy that stream beforehand.","If the payload may come from a non-Parcel source, use a byte[]/proto-based marshaller for that path instead of the Parcelable marshaller.","In code that accepts an InputStream, check `instanceof ParcelableInputStream` and route plain streams to a different deserializer before calling parseStream."],"exampleFix":"// before\nP value = marshaller.parseStream(inputStream);\n// after\nif (!(inputStream instanceof ParcelableInputStream)) {\n  throw new IllegalArgumentException(\"Parcelable marshaller requires a ParcelableInputStream\");\n}\nP value = marshaller.parseStream(inputStream);","handlingStrategy":"type-guard","validationCode":"if (!(stream instanceof ParcelableInputStream)) {\n  throw new IllegalArgumentException(\"Parcelable marshaller requires ParcelableInputStream\");\n}","typeGuard":"boolean isParcelableStream(InputStream s) { return s instanceof ParcelableInputStream; }","tryCatchPattern":"try {\n  P p = marshaller.parseStream(stream);\n} catch (UnsupportedOperationException e) {\n  // route to byte-stream marshaller\n}","preventionTips":["Never wrap or copy the binder transport stream before parseStream.","Use byte/proto marshallers for non-Parcel data paths.","Keep Parcelable marshaller usage confined to the binder transport."],"tags":["android","parcelable","serialization","unsupported-operation"],"backgroundTag":"incompatible-source-type","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}