Skip to content

Commit 0495310

Browse files
committed
fix: handle unbound response_or_error in send_request timeout
Initialize response_or_error to None before the try block and add an explicit None check after. This prevents an UnboundLocalError when anyio's fail_after cancel scope suppresses the timeout exception at the exact deadline boundary without raising, leaving the variable unassigned (see agronholm/anyio#589). Github-Issue: #1717 Reported-by: maxisbey
1 parent 883d893 commit 0495310

File tree

1 file changed

+10
-0
lines changed

1 file changed

+10
-0
lines changed

src/mcp/shared/session.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ async def send_request(
270270
# request read timeout takes precedence over session read timeout
271271
timeout = request_read_timeout_seconds or self._session_read_timeout_seconds
272272

273+
response_or_error: JSONRPCResponse | JSONRPCError | None = None
273274
try:
274275
with anyio.fail_after(timeout):
275276
response_or_error = await response_stream_reader.receive()
@@ -278,6 +279,15 @@ async def send_request(
278279
message = f"Timed out while waiting for response to {class_name}. Waited {timeout} seconds."
279280
raise MCPError(code=REQUEST_TIMEOUT, message=message)
280281

282+
if response_or_error is None:
283+
raise MCPError(
284+
code=REQUEST_TIMEOUT,
285+
message=(
286+
f"Response not received for {request.__class__.__name__}. "
287+
"This may indicate a race condition in the cancel scope."
288+
),
289+
)
290+
281291
if isinstance(response_or_error, JSONRPCError):
282292
raise MCPError.from_jsonrpc_error(response_or_error)
283293
else:

0 commit comments

Comments
 (0)