fix(mcp): stop HTTP status detection from matching data in exception text - #1913
Merged
Aaronontheweb merged 2 commits intoAug 13, 2026
Merged
Conversation
McpClientManager.FindHttpStatus checked an exception message for the bare text "401" and "403". An exception message can hold other data, like a command name or a GUID. A GUID that held "401" made the method report the wrong HTTP status. This defect caused two problems. - A flaky test. The test builds a stdio command name from a random GUID. The test fails when the GUID holds "401" or "403". - A wrong report. A stdio server with "401" or "403" in its command path shows as an HTTP auth failure in `netclaw mcp list` and `netclaw doctor`. The fix has two parts. - FindHttpStatus now skips status detection for stdio transport failures. A stdio server is a local process. No HTTP request occurs for it. - FindHttpStatus no longer checks the bare text "401" or "403". It trusts only a typed `HttpRequestException.StatusCode`, or an anchored text pattern like "HTTP 401" or "status Unauthorized". CreateSafeOAuthError calls FindHttpStatus too, so it gets the same fix. A new test proves the fix. A stdio spawn failure with "401" in the command name now reports as "Failed to reach MCP server. Check daemon logs for details." It does not report as an HTTP 401 failure. The test fails on the old code. The test passes on the new code.
Aaronontheweb
enabled auto-merge (squash)
August 13, 2026 03:25
Aaronontheweb
disabled auto-merge
August 13, 2026 12:52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
McpClientManager.FindHttpStatuswalked an exception chain and read anHTTP status from bare text:
Contains("401")andContains("403"). Anexception message can hold other data, like a command name or a temp
path. That data can hold digits that look like a status code by
chance.
This defect caused two problems.
FailedStdioStartup_IsReportedBeforeLeaseAssertionsbuilds a fake stdio command name from a random GUID. When the GUID
holds
401or403, the stdio spawn failure misreports as an HTTPauth failure, so the test asserts the wrong message. CI run
31659263924 shows the failure with GUID
632401b4.... The odds of ahit are about 1.4% per run: a
:N-formatted GUID has 32 hex digitsand 30 three-digit start positions, each with a 1-in-4096 chance of
spelling
401, and the same again for403, so the combined oddsround to 1.4%. Root cause was verified by two independent specialist
analyses; see the comment trail on fix(providers): detect a mid-stream LLM stall within seconds #1888.
401or
403in its command or file path shows as an HTTP auth failure innetclaw mcp listandnetclaw doctor, hiding the real spawn error.A sibling method,
CreateSafeOAuthError, calls the sameFindHttpStatusand carried the same risk for messages that embedGUID-bearing temp paths.
Fix
Two layers, applied together.
server is a local child process. No HTTP request ever occurs for it,
so no HTTP status can be genuine.
BuildConnectionFailureStatusalready receives the server's
TransportthroughMcpServerEntry,so this needed no new parameter — just a check at the top of the
method, threaded through
CreateUnreachableStatusandGetSafeConnectionFailure.Contains("401")/Contains("403")checks inFindHttpStatus. The method now trusts only two anchored shapes:a typed
HttpRequestException.StatusCode, or literal text like"HTTP 401"/"status Unauthorized".CreateSafeOAuthErrorinherits the fix automatically, since it calls the same method.
No genuine 401/403 detection test needed the bare substring pattern.
Every existing HTTP-failure test constructs its exception with a typed
HttpStatusCode(caught by the first branch), and the one message-onlycase (
DynamicRegistrationBadRequestIsNotMisreportedAsOuterUnauthorizedChallenge)already used the anchored
"status BadRequest"shape. Decompiling theMCP SDK confirmed
HttpResponseMessageExtensions.CreateHttpRequestExceptionand
McpOAuthClientRegistrarboth set the typedHttpRequestException.StatusCodewhenever they report a real HTTP failure, so the anchored/typed checks
already cover every real-world shape.
Tests
BuildConnectionFailureStatus_ForStdioSpawnFailureWithEmbeddedStatusLikeDigits_ReturnsUnreachable.Feeds a stdio spawn failure whose command name embeds
401(mirrorsthe real GUID from CI run 31659263924). Fails against the old code
(asserts the HTTP 401 string), passes against the fix.
command builder, pointing at this fix and the new unit test. The GUID
generation itself is unchanged, so the flaky test still proves the
fix on its own terms.
Validation
dotnet build Netclaw.slnx -c Release— 0 warnings, 0 errors.dotnet test src/Netclaw.Daemon.Tests— 1021 passed, 0 failed.dotnet slopwatch analyze -d .— 0 new violations (1 pre-existingbaselined warning, unrelated file).
./scripts/Add-FileHeaders.ps1 -Verify— all files have headers.FailedStdioStartup_IsReportedBeforeLeaseAssertionsrun 25 times ina loop — 25/25 passed.