apache / apache/dubbo

[Bug] hessian2 loses Byte/Short/Float element types in nested generic collections (List<List<Byte>>, Map<String, List<Byte>>)

Open
#16,440 2 comments 0 reactions 0 assignees View on GitHub
component/need-triage type/need-triage
Dominant language
Java
Stars
41.6k
Forks
26.4k
Avg merge
15h 13m
Merged PRs (30d)
4

Description

### Pre-check

- [x] I am sure that all the content I provide is in English.

### Search before asking

- [x] I had searched in the [issues](https://github.com/apache/dubbo/issues?q=is%3Aissue) and found no similar issues.

### Apache Dubbo Component

Java SDK (apache/dubbo)

### Dubbo Version

Dubbo Java 3.3.6 (latest release, reproduced), also 3.3.7-SNAPSHOT (main branch).
OpenJDK 21, Ubuntu 20.04
Protocol: dubbo (hessian2) and triple (hessian2 / hessian4).

### Steps to reproduce this issue

Typical production scenario: a service accepting binary chunks keyed by name.

public interface UploadService {
String uploadChunks(Map> chunks);
}

1. Consumer sends `Map>` whose inner lists contain
`(byte) 1, (byte) 127, (byte) -1`.
2. Provider reads the argument and does a strong-typed access:
`byte first = chunk.get(0);`

Reproduced on the latest release (Dubbo 3.3.6) with a real dubbo:// + hessian2
provider/consumer demo. On the provider, the argument deserializes with inner
elements widened to `Integer` instead of `Byte`:

[provider] received "c1" = ArrayList, first element runtime type = java.lang.Integer

The typed access then throws. The provider-side stack trace (dubbo frames
included, trimmed in the middle for readability):

java.lang.ClassCastException: class java.lang.Integer cannot be cast to
class java.lang.Byte (java.lang.Integer and java.lang.Byte are in module
java.base of loader 'bootstrap')
class java.lang.Byte (java.lang.Integer and java.lang.Byte are in module
java.base of loader 'bootstrap')
at demo.provider.UploadServiceImpl.uploadChunks(UploadServiceImpl.java:21)
at demo.provider.UploadServiceImplDubboWrap0.invokeMethod(UploadServiceImplDubboWrap0.java)
at org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:89)
at org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:100)
at org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker.invoke(DelegateProviderMetaDataInvoker.java:55)
... (dubbo server filter chain, trimmed) ...
at org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:167)
at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:110)
at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:205)
at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52)
at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:64)

Dubbo's own provider-side log reports it the same way (unchecked, undeclared):

ERROR ExceptionFilter - Got unchecked and undeclared exception ... service:
demo.api.UploadService, method: uploadChunks, exception:
java.lang.ClassCastException ... error code: 5-36

Typical production scenario 2 (same root cause): nested generic collections,
e.g. `List>` chunked byte streams or `Map>`
metric values — inner elements are widened to `Integer`/`Double` the same way.

### What you expected to happen

Provider receives `Map>` whose inner lists contain
`java.lang.Byte` elements, so the typed read works and no ClassCastException
is thrown. Same for the other affected generic shapes (scenario 2).

### Anything else

This is likely related to the generic-type handling addressed in #16197/#16198,
but it still reproduces on the latest release 3.3.6 and on the latest 3.3 main.

Root cause: hessian2 wire format encodes Byte/Short/Integer all as int and
Float/Double as double, so the element type can only be restored from the
declared generic type. On RPC top-level arguments Dubbo deserializes with only
the erased Class, so narrow element types are widened.

Workarounds: use `byte[]` instead of `List`; or `List`/`List`/
`List` which do not lose type; or switch to `fastjson2` which is
self-describing.

### Do you have a (mini) reproduction demo?

- [x] Yes, I have a minimal reproduction demo to help resolve this issue more effectively!

### Are you willing to submit a pull request to fix on your own?

- [ ] Yes I am willing to submit a pull request on my own!

### Code of Conduct

- [x] I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct)

Contributor guide

Open the contributing guide

Research direction

Start with the minimal Java provider/consumer reproduction and the UploadServiceImpl.java stack-trace entry point, then inspect Hessian2 generic-type handling related to #16197/#16198 and top-level RPC argument deserialization. Done means nested List and Map values preserve declared Byte, Short, and Float element types, with regression coverage for the reported shapes.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend-api-design, distributed-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.