apache/dubbo · error · IllegalStateException
Bad stream method signature. method(${methodName}:${paramDes
Error message
Bad stream method signature. method(${methodName}:${paramDesc}) What it means
In ReflectionMethodDescriptor.determineRpcType(), Dubbo inspects a method to classify it as UNARY, SERVER_STREAM, or BI_STREAM based on StreamObserver parameters/returns. After the recognized stream patterns are exhausted, if the method still uses a StreamObserver in a way that matches no valid pattern, it is malformed and rejected. This protects the streaming protocol from ambiguous dispatch at export time.
Source
Thrown at dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ReflectionMethodDescriptor.java:116
}
boolean returnIsVoid = returnClass.getName().equals(void.class.getName());
if (returnIsVoid && parameterClasses.length == 1 && isStreamType(parameterClasses[0])) {
actualRequestTypes = Collections.emptyList().toArray(new Class<?>[0]);
actualResponseType = obtainActualTypeInStreamObserver(
((ParameterizedType) method.getGenericParameterTypes()[0]).getActualTypeArguments()[0]);
return RpcType.SERVER_STREAM;
}
if (returnIsVoid
&& parameterClasses.length == 2
&& !isStreamType(parameterClasses[0])
&& isStreamType(parameterClasses[1])) {
actualRequestTypes = parameterClasses;
actualResponseType = obtainActualTypeInStreamObserver(
((ParameterizedType) method.getGenericParameterTypes()[1]).getActualTypeArguments()[0]);
return RpcType.SERVER_STREAM;
}
if (Arrays.stream(parameterClasses).anyMatch(this::isStreamType) || isStreamType(returnClass)) {
throw new IllegalStateException(
"Bad stream method signature. method(" + methodName + ":" + paramDesc + ")");
}
// Can not determine client stream because it has same signature with bi_stream
return RpcType.UNARY;
}
private boolean isStreamType(Class<?> classType) {
return StreamObserver.class.isAssignableFrom(classType);
}
@Override
public String getMethodName() {
return methodName;
}
@Override
public String getJavaMethodName() {
return method.getName();View on GitHub (pinned to 3a3043227f)
Solutions
- Conform the method to a supported stream signature: BI_STREAM = `StreamObserver<Resp> foo(StreamObserver<Req>)`; SERVER_STREAM = `void foo(StreamObserver<Resp>)` or `void foo(Req, StreamObserver<Resp>)`.
- Ensure the return type is exactly void for SERVER_STREAM, or a StreamObserver for BI_STREAM, and that StreamObserver is parameterized (not raw).
- Reduce parameters to at most 2 and ensure non-StreamObserver params do not exceed the pattern (0 or 1 request object).
- If the method is intended as unary, remove the StreamObserver parameter entirely.
Example fix
// before (throws: 3 params, ambiguous) StreamObserver<Resp> foo(Req r, Meta m, StreamObserver<Req> so); // after (valid BI_STREAM) StreamObserver<Resp> foo(StreamObserver<Req> so); // or valid SERVER_STREAM void foo(Req r, StreamObserver<Resp> so);
Defensive patterns
Strategy: validation
Validate before calling
// Validate a stream method matches a supported shape before export.
boolean ok =
(returnIsVoid && params == 1 && isStreamObserver(params[0])) || // SERVER_STREAM
(returnIsVoid && params == 2 && !isStreamObserver(params[0]) && isStreamObserver(params[1])) || // SERVER_STREAM
(params == 1 && isStreamObserver(params[0]) && isStreamObserver(returnType)); // BI_STREAM
if (!ok && usesAnyStreamObserver(method)) {
throw new IllegalArgumentException("Invalid stream signature: " + method);
} Prevention
- Keep stream methods to the documented shapes (0-1 request param + 1 StreamObserver, void return; or single StreamObserver in and out).
- Always parameterize StreamObserver<Req>/<Resp>; avoid raw StreamObserver.
- Do not exceed 2 parameters on a streaming method.
- Unit-test service interfaces against a signature validator during build.
When it happens
Trigger: A service interface method uses org.apache.dubbo.common.stream.StreamObserver but its signature does not fit any supported shape: e.g. a non-void return with a single StreamObserver param that is not itself a StreamObserver, three parameters, a StreamObserver as a return type with non-matching params, or raw/non-parameterized StreamObserver usages. Also triggered by >2 parameters where one is a StreamObserver.
Common situations: Writing a Dubbo Triple/streaming service by hand and getting the method shape wrong (extra params, wrong return type). Copying a gRPC-style signature without adjusting to Dubbo's StreamObserver contract. Using a raw `StreamObserver` without generic type arguments. Mixing streaming and unary params on the same method.
Related errors
- Stream method could not be overloaded.There are ${streamMeth
- ${cls.getName()} generic type undefined!
- windowUpdateRatio must be > 0 and <= 1, but was: <windowUpda
- The interface class <interfaceClass> is not a interface!
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/37692cb499f3d981.
Report an issue: GitHub.