[I] [BUG] concurrent-tool-calls [shenyu]</span></a></span> </h1> <p class="darkgray font13"> <span class="sender pipe"><a href="/search?l=notifications@shenyu.apache.org&q=from:%22via+GitHub%22" rel="nofollow"><span itemprop="author" itemscope itemtype="http://schema.org/Person"><span itemprop="name">via GitHub</span></span></a></span> <span class="date"><a href="/search?l=notifications@shenyu.apache.org&q=date:20260907" rel="nofollow">Mon, 07 Sep 2026 18:02:09 -0700</a></span> </p> </div> <div itemprop="articleBody" class="msgBody"> <!--X-Body-of-Message--> <pre> baili123 opened a new issue, #7041: URL: <a rel="nofollow" href="https://github.com/apache/shenyu/issues/7041">https://github.com/apache/shenyu/issues/7041</a></pre><pre> ### Is there an existing issue for this? - [x] I have searched the existing issues ### Current Behavior ## Bug Report ### Which version of ShenYu? master (verified against `f7602e324`). The affected code has been unchanged since it was introduced in `e2cb6f3ab` (2025-07-15, #5999). ### Expected behavior Two or more MCP `tools/call` requests issued concurrently within the same MCP session should each be proxied independently and return their own result. MCP clients routinely issue parallel tool calls, and at the transport level each call is already a separate HTTP POST carrying its own JSON-RPC `id`. ### Actual behavior **Every concurrent tool call fails.** Not a rare race — a 100% failure rate in my tests. Failures surface as three different errors that all look like downstream/network problems: ``` {"code":-103,"message":"Service invocation exception, or no result is returned!"} {"code":-106,"message":"Can not find url, please check your configuration!"} "" (empty response) Tool execution failed: ... NullPointerException: Cannot invoke "java.lang.Long.longValue()" ``` Responses can also be truncated mid-JSON: ``` {"jsonrpc":"2.0","id":"p-P1","result":{"content":[{"type":"text","text":"{\"code\ ^ stream cut, 81 bytes total ``` ### How to reproduce 1. Configure an `mcpServer` selector with one tool whose `requestConfig` proxies a POST endpoint that echoes its request body, e.g. ```json { "name": "echo_tool", "parameters": [{ "name": "note", "type": "string", "description": "echoed back" }], "requestConfig": "{\"requestTemplate\":{\"url\":\"/echo\",\"method\":\"POST\",\"argsToJsonBody\":true,\"headers\":[]},\"argsPosition\":{\"note\":\"body\"}}" } ``` 2. Open one MCP session: ```bash GW=http://<gateway-host>:9195/<mcp-path>/streamablehttp SID=$(curl -sD- -o/dev/null -X POST "$GW" \ -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"probe","version":"1.0"}}}' \ | grep -i '^Mcp-Session-Id:' | tr -d '\r' | awk '{print $2}') curl -s -o/dev/null -X POST "$GW" -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' -H "Mcp-Session-Id: $SID" \ -d '{"jsonrpc":"2.0","method":"notifications/initialized"}' ``` 3. Baseline — call the tool **serially** twice with distinct `note` values. Both succeed and each response carries its own `note`. 4. Now fire three calls **concurrently on the same session**: ```bash for n in X Y Z; do curl -s -X POST "$GW" -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' -H "Mcp-Session-Id: $SID" \ -d "{\"jsonrpc\":\"2.0\",\"id\":\"c-$n\",\"method\":\"tools/call\",\"params\":{\"name\":\"echo_tool\",\"arguments\":{\"note\":\"$n\"}}}" & done; wait ``` ### Results observed | scenario | outcome | | --- | --- | | 2 serial calls (before concurrency) | 2/2 correct, each response matched its own `note` | | **3 concurrent × 3 rounds** | **9/9 failed, 0 succeeded** | | **2 concurrent** | **2/2 failed** (one `-103`, one truncated response) | | 2 serial calls (after concurrency) | 2/2 correct — the session is not poisoned; failures are strictly concurrent-only | ### Root cause Each concurrent tool call arrives as its own HTTP POST and therefore already has its own `ServerWebExchange`. That isolation is then discarded: the exchange is stored in a static map keyed by **session id**, so N concurrent requests collapse into one slot. `ShenyuMcpExchangeHolder`: ```java private static final Map<String, ServerWebExchange> EXCHANGE_MAP = new ConcurrentHashMap<>(); public static void put(final String sessionId, final ServerWebExchange exchange) { EXCHANGE_MAP.put(sessionId, exchange); // later request overwrites the earlier one } ``` `ShenyuStreamableHttpServerTransportProvider#configureExchangeForSession` (line 566) stores every POST's exchange under that single key, and `ShenyuToolCallback#call` (line 134) reads it back by session id: ```java final String sessionId = extractSessionId(mcpExchange); final ServerWebExchange originExchange = getOriginExchange(sessionId); final ShenyuPluginChain chain = getPluginChain(originExchange); ``` Because the tool call reuses the **inbound** exchange and replays the plugin chain on it, all per-request state lives on that now-shared object and concurrent calls overwrite each other's attributes. Each observed error maps to one clobbered attribute: | error | attribute lost | site | | --- | --- | --- | | `-106 Can not find url` | `HTTP_URI` (written by `URIPlugin`) | `AbstractHttpClientPlugin:67` | | `-103 no result` | `CLIENT_RESPONSE_CONN_ATTR` | `NettyClientMessageWriter:60` | | empty / truncated body | response written by two writers | `NettyClientMessageWriter` `response.writeWith(body)` | ### Suggested fix Either of: 1. Key the holder by the JSON-RPC **request id** (or any per-call token) instead of the session id, and clean the entry up when the call completes. MCP explicitly allows concurrent in-flight requests per session, which is exactly what the JSON-RPC `id` is for. 2. Do not reuse the inbound exchange at all — build a fresh outbound request per tool call rather than mutating and replaying the inbound one. Option 2 also removes the need for the blocking wait in `ShenyuToolCallback:270` (`responseFuture.get(60, SECONDS)`), which currently blocks inside a reactive pipeline. ### Notes Since the per-tool-call timeout here is 60s while a `divide` rule with the default `retry = 3` can take `4 × timeout`, the two limits can also disagree; that is a separate, smaller concern. ### Expected Behavior _No response_ ### Steps To Reproduce _No response_ ### Environment ```markdown ShenYu version(s): ``` ### Debug logs _No response_ ### Anything else? _No response_ -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: notifications-unsubscr...@shenyu.apache.org.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org </pre> </div> <div class="msgButtons margintopdouble"> <ul class="overflow"> <li class="msgButtonItems"><a class="button buttonleft " accesskey="p" href="msg37282.html">Previous message</a></li> <li class="msgButtonItems textaligncenter"><a class="button" accesskey="c" href="thrd7.html#37284">View by thread</a></li> <li class="msgButtonItems textaligncenter"><a class="button" accesskey="i" href="mail9.html#37284">View by date</a></li> <li class="msgButtonItems textalignright"><a class="button buttonright " accesskey="n" href="msg37283.html">Next message</a></li> </ul> </div> <a name="tslice"></a> <div class="tSliceList margintopdouble"> <ul class="icons monospace"> <li class="icons-email tSliceCur"><span class="subject">[I] [BUG] <title>concurrent-tool-calls [shenyu]</span> <span class="sender italic">via GitHub</span></li> <li><ul> <li class="icons-email"><span class="subject"><a href="msg37283.html">Re: [I] [BUG] <title>concurrent-tool-calls [shenyu]</a></span> <span class="sender italic">via GitHub</span></li> <li class="icons-email"><span class="subject"><a href="msg37288.html">Re: [I] [BUG] <title>concurrent-tool-calls [shenyu]</a></span> <span class="sender italic">via GitHub</span></li> <li class="icons-email"><span class="subject"><a href="msg37292.html">Re: [I] [BUG] <title>concurrent-tool-calls [shenyu]</a></span> <span class="sender italic">via GitHub</span></li> <li class="icons-email"><span class="subject"><a href="msg37300.html">Re: [I] [BUG] <title>concurrent-tool-calls [shenyu]</a></span> <span class="sender italic">via GitHub</span></li> <li class="icons-email"><span class="subject"><a href="msg38809.html">Re: [I] [BUG] <title>concurrent-tool-calls [shenyu]</a></span> <span class="sender italic">via GitHub</span></li> </ul> </ul> </div> <div class="overflow msgActions margintopdouble"> <div class="msgReply" > <h2> Reply via email to </h2> <form method="POST" action="/mailto.php"> <input type="hidden" name="subject" value="[I] [BUG] <title>concurrent-tool-calls [shenyu]"> <input type="hidden" name="msgid" value="I_kwDOCGCHjs8AAAABQLQwww@gitbox.apache.org"> <input type="hidden" name="relpath" value="notifications@shenyu.apache.org/msg37284.html"> <input type="submit" value=" via GitHub "> </form> </div> </div> </div> <div class="aside" role="complementary"> <div class="logo"> <a href="/"><img src="/logo.png" width=247 height=88 alt="The Mail Archive"></a> </div> <form class="overflow" action="/search" method="get"> <input type="hidden" name="l" value="notifications@shenyu.apache.org"> <label class="hidden" for="q">Search the site</label> <input class="submittext" type="text" id="q" name="q" placeholder="Search notifications"> <input class="submitbutton" name="submit" type="image" src="/submit.png" alt="Submit"> </form> <div class="nav margintop" id="nav" role="navigation"> <ul class="icons font16"> <li class="icons-home"><a href="/">The Mail Archive home</a></li> <li class="icons-list"><a href="/notifications@shenyu.apache.org/">notifications - all messages</a></li> <li class="icons-about"><a href="/notifications@shenyu.apache.org/info.html">notifications - about the list</a></li> <li class="icons-expand"><a href="/search?l=notifications@shenyu.apache.org&q=subject:%22%5C%5BI%5C%5D+%5C%5BBUG%5C%5D+%3Ctitle%3Econcurrent%5C-tool%5C-calls+%5C%5Bshenyu%5C%5D%22&o=newest&f=1" title="e" id="e">Expand</a></li> <li class="icons-prev"><a href="msg37282.html" title="p">Previous message</a></li> <li class="icons-next"><a href="msg37283.html" title="n">Next message</a></li> </ul> </div> <div class="listlogo margintopdouble"> </div> <div class="margintopdouble"> </div> </div> </div> <div class="footer" role="contentinfo"> <ul> <li><a href="/">The Mail Archive home</a></li> <li><a href="/faq.html#newlist">Add your mailing list</a></li> <li><a href="/faq.html">FAQ</a></li> <li><a href="/faq.html#support">Support</a></li> <li><a href="/faq.html#privacy">Privacy</a></li> <li class="darkgray">I_kwDOCGCHjs8AAAABQLQwww@gitbox.apache.org</li> </ul> </div> </body> </html>