This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new 7a1b8eeb4 fix(rmqctl): ignore non-positive --timeout instead of 
failing every request (#4627)
7a1b8eeb4 is described below

commit 7a1b8eeb47ba3bc2e701ddffd49938de988e6c22
Author: Apulupie <[email protected]>
AuthorDate: Mon Sep 21 21:07:02 2026 +0800

    fix(rmqctl): ignore non-positive --timeout instead of failing every request 
(#4627)
    
    `rmqctl/internal/studio/client.go` passed `target.Timeout` straight into 
`context.WithTimeout`, so `--timeout 0s` — the usual spelling of "no client 
timeout" — produced a deadline that had already expired and every call failed 
with `context deadline exceeded` before the request left. `--timeout` is a 
cobra `DurationVar` with no positivity check, `runtime.go` copies it into 
`Target.Timeout` verbatim, and `error.go` maps the resulting timeout code to 
"Increase --timeout", pointing the u [...]
    
    `CallTool` now derives the context with `WithCancel` when the timeout is 
not positive, making the two paths agree that a non-positive value means no 
client-side deadline rather than falling back to a default. These were the only 
two `context.WithTimeout` call sites in the client.
    
    Fixes #4628
---
 rmqctl/internal/studio/client.go     | 10 +++++++++-
 rmqctl/internal/studio/tools_test.go | 24 ++++++++++++++++++++++++
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/rmqctl/internal/studio/client.go b/rmqctl/internal/studio/client.go
index 6eb24f376..04ec2c56d 100644
--- a/rmqctl/internal/studio/client.go
+++ b/rmqctl/internal/studio/client.go
@@ -114,7 +114,15 @@ func (c Client) request(ctx context.Context, target 
Target, method string, path
                }
                reader = bytes.NewReader(payload)
        }
-       ctx, cancel := context.WithTimeout(ctx, target.Timeout)
+       // A non-positive timeout must not be passed to context.WithTimeout, 
which would expire
+       // the context immediately and fail every request. Mirror 
mcp_message.go and treat it as
+       // "no client-side timeout".
+       var cancel context.CancelFunc
+       if target.Timeout > 0 {
+               ctx, cancel = context.WithTimeout(ctx, target.Timeout)
+       } else {
+               ctx, cancel = context.WithCancel(ctx)
+       }
        defer cancel()
        requestURL, err := url.JoinPath(target.BaseURL(), path)
        if err != nil {
diff --git a/rmqctl/internal/studio/tools_test.go 
b/rmqctl/internal/studio/tools_test.go
index e7c535ffc..34aaf4169 100644
--- a/rmqctl/internal/studio/tools_test.go
+++ b/rmqctl/internal/studio/tools_test.go
@@ -63,6 +63,30 @@ func TestCallTool(t *testing.T) {
        }
 }
 
+func TestCallToolIgnoresNonPositiveTimeout(t *testing.T) {
+       server := httptest.NewServer(http.HandlerFunc(func(w 
http.ResponseWriter, r *http.Request) {
+               _ = json.NewEncoder(w).Encode(map[string]any{
+                       "code": 200, "message": "success",
+                       "data": map[string]any{"items": []any{}},
+               })
+       }))
+       defer server.Close()
+
+       for name, timeout := range map[string]time.Duration{"zero": 0, 
"negative": -time.Second} {
+               t.Run(name, func(t *testing.T) {
+                       client := NewClient(server.Client())
+                       result, err := client.CallTool(context.Background(), 
Target{
+                               Server: server.URL, InstanceID: "instance-dev",
+                               Credential: Credential{AccessKey: "test-ak", 
SecretKey: "test-sk"}, Timeout: timeout,
+                       }, "rmq.topic.list", map[string]any{"instanceId": 
"instance-dev"})
+                       resultMap, ok := result.(map[string]any)
+                       if err != nil || !ok || resultMap["items"] == nil {
+                               t.Fatalf("result=%#v err=%v", result, err)
+                       }
+               })
+       }
+}
+
 func TestCallToolKeepsCatalogControlsInArguments(t *testing.T) {
        server := httptest.NewServer(http.HandlerFunc(func(w 
http.ResponseWriter, r *http.Request) {
                var request types.ToolCallRequest

Reply via email to