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

  1. 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>)`.
  2. Ensure the return type is exactly void for SERVER_STREAM, or a StreamObserver for BI_STREAM, and that StreamObserver is parameterized (not raw).
  3. Reduce parameters to at most 2 and ensure non-StreamObserver params do not exceed the pattern (0 or 1 request object).
  4. 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

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


AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14). Data as JSON: /api/errors/37692cb499f3d981. Report an issue: GitHub.