dotnet/aspnetcore · error · RuntimeException

'%s' already has a value returning handler. Multiple return

Error message

'%s' already has a value returning handler. Multiple return values are not supported.

What it means

CallbackMap stores handlers per hub method target. When you register an `on` handler whose action returns a value (getHasResult()==true), the map forbids a second value-returning handler for the same target, because the client can only send one result back to the server for a server-to-client invocation.

Source

Thrown at src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/CallbackMap.java:30

class CallbackMap {
    private final Map<String, List<InvocationHandler>> handlers = new HashMap<>();
    private final ReentrantLock lock = new ReentrantLock();

    public InvocationHandler put(String target, Object action, Type... types) {
        try {
            lock.lock();
            InvocationHandler handler = new InvocationHandler(action, types);
            if (!handlers.containsKey(target)) {
                handlers.put(target, new ArrayList<>());
            }

            List<InvocationHandler> methodHandlers;
            methodHandlers = handlers.get(target);
            if (handler.getHasResult()) {
                for (InvocationHandler existingHandler : methodHandlers) {
                    if (existingHandler.getHasResult()) {
                        throw new RuntimeException(String.format("'%s' already has a value returning handler. Multiple return values are not supported.", target));
                    }
                }
            }
            methodHandlers = new ArrayList<>(methodHandlers);
            methodHandlers.add(handler);

            // replace List in handlers map
            handlers.remove(target);
            handlers.put(target, methodHandlers);
            return handler;
        } finally {
            lock.unlock();
        }
    }

    public List<InvocationHandler> get(String key) {
        try {
            lock.lock();

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Keep at most one value-returning handler per method name; remove (hubConnection.remove(name, handler)) before re-registering.
  2. Use hubConnection.on(...) once during connection setup and dispose handlers on teardown.
  3. If multiple consumers need the same method, route through a single dispatcher that returns one value.
  4. Audit registration paths (reconnect callbacks, DI, lifecycle hooks) for accidental double-registration.

Example fix

// before
hubConnection.on("Calc", (int a) -> a + 1, int.class);
hubConnection.on("Calc", (int a) -> a * 2, int.class); // throws

// after
hubConnection.remove("Calc", firstHandler);
hubConnection.on("Calc", (int a) -> a * 2, int.class);
Defensive patterns

Strategy: validation

Validate before calling

Set<String> returningTargets = new HashSet<>();
void register(String target, Object action, Type... types) {
  InvocationHandler h = new InvocationHandler(action, types);
  if (h.getHasResult() && returningTargets.contains(target)) {
    throw new IllegalStateException("Returning handler already registered for " + target);
  }
  if (h.getHasResult()) returningTargets.add(target);
  // then hubConnection.on(...)
}

Try / catch

try {
  hubConnection.on("Method", action, types);
} catch (RuntimeException e) {
  if (e.getMessage().contains("already has a value returning handler")) {
    hubConnection.remove("Method", previousHandler);
    hubConnection.on("Method", action, types);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling hubConnection.on("MethodName", actionWithType) twice for the same MethodName where both handlers return a value; or calling on(...).on(...) after removing one but the prior returning handler still lingers.

Common situations: Duplicate registration during a reconnect or re-init, a refactor that registers the same handler in two places, or a handler registered both globally and in a component without cleanup.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/4194c134f659da2f. Report an issue: GitHub.