grpc/grpc-java · error · UnsupportedOperationException
Can't unmarshall a parcelable from a regular byte stream
Error message
Can't unmarshall a parcelable from a regular byte stream
What it means
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.
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.
Example fix
// before
P value = marshaller.parseStream(inputStream);
// after
if (!(inputStream instanceof ParcelableInputStream)) {
throw new IllegalArgumentException("Parcelable marshaller requires a ParcelableInputStream");
}
P value = marshaller.parseStream(inputStream); Defensive patterns
Strategy: type-guard
Validate before calling
if (!(stream instanceof ParcelableInputStream)) {
throw new IllegalArgumentException("Parcelable marshaller requires ParcelableInputStream");
} Type guard
boolean isParcelableStream(InputStream s) { return s instanceof ParcelableInputStream; } Try / catch
try {
P p = marshaller.parseStream(stream);
} catch (UnsupportedOperationException e) {
// route to byte-stream marshaller
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- call forAddress(AndroidComponentAddress, Context) instead
- call forAddress() instead
- call forAddress() instead
- call forAddress(String, int, CronetEngine) instead
- TLS not supported in BinderServer
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/6ee065ba2498465d.
Report an issue: GitHub.
Appendix: source
Thrown at binder/src/main/java/io/grpc/binder/internal/MetadataHelper.java:223
public ParcelableMetadataMarshaller(
@Nullable Parcelable.Creator<P> creator, boolean immutableType) {
this.creator = creator;
this.immutableType = immutableType;
}
@Override
public InputStream toStream(P value) {
return new ParcelableInputStream<>(creator, value, immutableType);
}
@Override
@SuppressWarnings("unchecked")
public P parseStream(InputStream stream) {
if (stream instanceof ParcelableInputStream) {
return ((ParcelableInputStream<P>) stream).getParcelable();
} else {
throw new UnsupportedOperationException(
"Can't unmarshall a parcelable from a regular byte stream");
}
}
}
}
View on GitHub (pinned to 64daddc1f3)