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).
This commit is contained in:
CK 2026-06-05 01:59:47 -05:00 • committed by GitHub
commit ef29673221
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 53 additions and 2 deletions

View file

@ -0,0 +1,21 @@
package v0
import (
"context"
"errors"
"log"
"github.com/danielgtaylor/huma/v2"
)
// ListServersError maps ListServers failures; client disconnects must not log as 500s.
func ListServersError(ctx context.Context, err error) error {
if err == nil {
return nil
}
if errors.Is(err, context.Canceled) || errors.Is(ctx.Err(), context.Canceled) {
return huma.NewError(499, "Client closed request", err)
}
log.Printf("list servers failed: %v", err)
return huma.Error500InternalServerError("Failed to get registry list", err)
}

View file

@ -0,0 +1,31 @@
package v0_test
import (
"context"
"errors"
"testing"
"github.com/danielgtaylor/huma/v2"
v0 "github.com/modelcontextprotocol/registry/internal/api/handlers/v0"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestListServersError_clientCanceled(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
err := v0.ListServersError(ctx, errors.New("error iterating rows: context canceled"))
require.Error(t, err)
var se huma.StatusError
require.True(t, errors.As(err, &se))
assert.Equal(t, 499, se.GetStatus())
}
func TestListServersError_realFailure(t *testing.T) {
err := v0.ListServersError(context.Background(), errors.New("database unavailable"))
require.Error(t, err)
var se huma.StatusError
require.True(t, errors.As(err, &se))
assert.Equal(t, 500, se.GetStatus())
}

View file

@ -132,8 +132,7 @@ func RegisterServersEndpoints(api huma.API, pathPrefix string, registry service.
// Get paginated results with filtering
servers, nextCursor, err := registry.ListServers(ctx, filter, input.Cursor, input.Limit)
if err != nil {
log.Printf("list servers failed: %v", err)
return nil, huma.Error500InternalServerError("Failed to get registry list")
return nil, ListServersError(ctx, err)
}
// Convert []*ServerResponse to []ServerResponse