Skip to content

Commit ef29673

Browse files
fix: client-cancelled GET /v0/servers returns 499 without error log (#1335)
Maps context.Canceled from ListServers to 499 and skips the error-level log on client disconnect. Merged as-is per maintainer decision; known follow-up tracked in #1337 (500-path error-detail leak + WriteHeader claim correction + nits).
1 parent 04623ed commit ef29673

3 files changed

Lines changed: 53 additions & 2 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package v0
2+
3+
import (
4+
"context"
5+
"errors"
6+
"log"
7+
8+
"github.com/danielgtaylor/huma/v2"
9+
)
10+
11+
// ListServersError maps ListServers failures; client disconnects must not log as 500s.
12+
func ListServersError(ctx context.Context, err error) error {
13+
if err == nil {
14+
return nil
15+
}
16+
if errors.Is(err, context.Canceled) || errors.Is(ctx.Err(), context.Canceled) {
17+
return huma.NewError(499, "Client closed request", err)
18+
}
19+
log.Printf("list servers failed: %v", err)
20+
return huma.Error500InternalServerError("Failed to get registry list", err)
21+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package v0_test
2+
3+
import (
4+
"context"
5+
"errors"
6+
"testing"
7+
8+
"github.com/danielgtaylor/huma/v2"
9+
v0 "github.com/modelcontextprotocol/registry/internal/api/handlers/v0"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestListServersError_clientCanceled(t *testing.T) {
15+
ctx, cancel := context.WithCancel(context.Background())
16+
cancel()
17+
18+
err := v0.ListServersError(ctx, errors.New("error iterating rows: context canceled"))
19+
require.Error(t, err)
20+
var se huma.StatusError
21+
require.True(t, errors.As(err, &se))
22+
assert.Equal(t, 499, se.GetStatus())
23+
}
24+
25+
func TestListServersError_realFailure(t *testing.T) {
26+
err := v0.ListServersError(context.Background(), errors.New("database unavailable"))
27+
require.Error(t, err)
28+
var se huma.StatusError
29+
require.True(t, errors.As(err, &se))
30+
assert.Equal(t, 500, se.GetStatus())
31+
}

internal/api/handlers/v0/servers.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,7 @@ func RegisterServersEndpoints(api huma.API, pathPrefix string, registry service.
132132
// Get paginated results with filtering
133133
servers, nextCursor, err := registry.ListServers(ctx, filter, input.Cursor, input.Limit)
134134
if err != nil {
135-
log.Printf("list servers failed: %v", err)
136-
return nil, huma.Error500InternalServerError("Failed to get registry list")
135+
return nil, ListServersError(ctx, err)
137136
}
138137

139138
// Convert []*ServerResponse to []ServerResponse

0 commit comments

Comments
 (0)