Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Server: a notifications/cancelled that arrives before the request's handler task is registered is dropped, so the request runs to completion

オープン
#285 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 7 日以内に返信

まだ誰も着手していません。

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
25/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
停滞
技術スタック
swift
領域
api, backend

調査の方向性

Start with Sources/MCP/Server/Server.swift at the receive-loop dispatch, handleRequest, and removePendingRequest(id:) locations described in the issue. Review PR #270 and its “Cancellation received before handler registration prevents handler start” barrier test; done means the reproduction no longer starts or responds to a request cancelled immediately after dispatch.

索引モデルが issue の本文から書いたものです。

説明

Summary

Server registers a request's handler task for cancellation after it has already returned to the receive loop, so a notifications/cancelled that follows the request closely enough is processed before the registration exists. The cancellation is then logged as "unknown or completed" and ignored, and the request runs to completion and sends its response — for a request the server has, in fact, received and is about to start.

Mechanics (0.12.1, Sources/MCP/Server/Server.swift)
  1. The receive loop decodes a request and hands it off without waiting (:235):
    } else if let request = try? decoder.decode(AnyRequest.self, from: data) {
        // Handle request in a separate task to avoid blocking the receive loop
        Task {
            _ = try? await self.handleRequest(request, sendResponse: true)
        }
    }
    
    The loop immediately continues to the next stream element.
  2. Inside that task, handleRequest first awaits the transport's HTTP context lookup (:784) and only then registers the task (:816):
    pendingRequestTasks[request.id] = handlerTask
    
  3. Meanwhile the loop has already decoded the next element. If it is notifications/cancelled for that id, the built-in handler runs removePendingRequest(id:) (:1001), finds nothing, and takes the "unknown or completed" branch. Nothing cancels the handler task once it is registered a moment later.

The window is the gap between the detached Task at :235 and the assignment at :816, which includes an await. It is a real ordering hazard, not a theoretical one: any client that sends a request and cancels it immediately (a user abort, a timeout that fires during send) can hit it, and it is deterministic to reproduce with a barrier that holds the context lookup until the cancellation has been consumed.

Expected behavior

The cancellation spec says the receiver SHOULD stop processing the cancelled request and not send a response, and MAY ignore a cancellation for an unknown request. This request is not unknown — the server decoded it and dispatched it — so ignoring the cancellation is a missed SHOULD caused by an implementation race, not a legitimate "unknown request" case. A cancellation that arrives after dispatch and before registration should still cancel the request, or prevent the handler from starting.

Reproduction

On any transport (stdio, or two back-to-back stateless HTTP POSTs), send:

  1. {"jsonrpc":"2.0","id":"r1","method":"tools/call","params":{"name":"slow"}}
  2. immediately: {"jsonrpc":"2.0","method":"notifications/cancelled","params":{"requestId":"r1"}}

Observe: the slow handler runs and a response for r1 is sent. With a transport whose httpRequestContext(for:) is held until after the cancellation is consumed, the outcome is deterministic.

Existing fix

PR #270 (Fix early request cancellation race) addresses exactly this: it tracks the dispatch task before handler registration so an immediate cancellation cannot fall through the gap, processes the built-in cancellation handler before other notification handlers can suspend, and adds a deterministic barrier test (Cancellation received before handler registration prevents handler start). That PR currently references no issue; this one is filed so the defect has a tracking entry independent of any particular fix. #270 also carries two adjacent hardening changes — suppressing a late response from a handler that swallows CancellationError, and rejecting duplicate outstanding request ids in Server — which are worth noting when reviewing it, since the second is a behavior change beyond this bug.

Related
  • #255 — a cancelled request's HTTP POST hangs in StatelessHTTPServerTransport (the transport side of cancellation; fix in #260)
  • #254 / #265 — colliding JSON-RPC ids across concurrent stateless clients (fix in #264); with exchange-scoped ids in place, Server's duplicate-id rejection from #270 only ever triggers on single-client transports
主要言語
Swift
スター
1.5k
フォーク
252
PR マージ指標
30日以内にマージされた PR はありません

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

modelcontextprotocol/swift-sdk のほかの issue

modelcontextprotocol/swift-sdk の issue をすべて見る

似ている issue

Swift の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。