Skip to content

MCP Toolkit backend stalls for hours in a Keychain call with no deadline (SecItemCopyMatching), Docker Desktop 4.94.0 on macOS 26.6.2 #732

Open

Description

@DigitalDalmatian

Bug report

Bug description

On Docker Desktop 4.94.0 (macOS 26.6.2, MCP Toolkit v0.44.1) the MCP Toolkit stops answering for hours while the engine stays healthy. docker version works; docker mcp oauth ls and every docker mcp gateway time out with Get "http://localhost/apps": context deadline exceeded. It happened on seven days out of eight (2026-09-30 to 2026-10-08), once clearing by itself. The menu's Restart only restarts the engine VM, so it never clears it. Sending SIGQUIT to com.docker.backend services (which writes a goroutine dump to the monitor log) and reopening Docker clears it every time. It was happening again at 17:28 ET on 2026-10-08 (backend up 12 h 16 min).

Expected: the toolkit keeps answering, or a stuck Keychain read times out and fails only that one request.

Reproduction steps

We cannot trigger it on demand. It has recurred about once a day on a Mac with four authorized OAuth providers and several MCP clients that each start their own docker mcp gateway run (about twenty such processes were running during the measurement below; we have not proven that is required). Once it is wedged, docker mcp oauth ls fails at its 10 s deadline while docker version answers at once.

Evidence 1: a Keychain call with no deadline, held under a lock

The goroutine dump from 2026-10-06 (284 goroutines) shows two goroutines inside SecItemCopyMatching for 673 minutes, and 106 goroutines blocked on mutexes behind them (the longest 673 minutes; 67 of them in docker-credential-desktop.(*cache).List). One stuck stack, trimmed to function names and build paths:

runtime.cgocall
github.com/docker/secrets-engine/store/keychain/internal/go-keychain._Cfunc_SecItemCopyMatching
github.com/docker/secrets-engine/store/keychain/internal/go-keychain.QueryItemRef      (go-keychain/keychain.go:526)
github.com/docker/secrets-engine/store/keychain/internal/go-keychain.QueryItem         (go-keychain/keychain.go:539)
github.com/docker/secrets-engine/store/keychain.getItemWithData[...]                   (keychain_darwin.go:93)
github.com/docker/secrets-engine/store/keychain.(*keychainStore[...]).loadSecret       (keychain_darwin.go:291)
github.com/docker/secrets-engine/store/keychain.(*keychainStore[...]).Filter           (keychain_darwin.go:278, 225)
github.com/docker/pinata/common/cmd/com.docker.backend/internal/store.(*service).Filter
github.com/docker/pinata/common/cmd/com.docker.backend/internal/oauthmanager/store.(*transformerStore).Filter
github.com/docker/pinata/common/cmd/com.docker.backend/internal/server.(*desktopPlugin).GetSecrets
github.com/docker/secrets-engine-private/runtime/internal/secrets/builtin.(*builtinProvider).GetSecrets

The same call was stuck in a second dump on 2026-10-08. Everything else in the backend stayed alive (engine proxy, vfkit, the periodic docker-credential-osxkeychain list polling), which is why docker version works while the toolkit's /apps API does not. Ask: put a deadline on every Keychain call, and do not hold the cache mutexes across it.

New measurement from a live jam (2026-10-08, 17:28 ET)

While the toolkit was jammed (backend process up 12 h 16 min, state S, about 7% CPU), two NEW processes used the same login keychain without trouble: docker-credential-osxkeychain list returned in 0.02 s and security show-keychain-info on the login keychain in 0.01 s (security show-keychain-info reports no-timeout). In the same minute docker mcp oauth ls hit its 10 s deadline. In the backend log's current segment (18 minutes) three GET /apps requests from the toolkit's API arrived and none was answered; no response line was written for any request on that API. So the Keychain service is answering and the stall is specific to the backend process. This does not show why the call stops returning.

Evidence 2: token refresh window (a separate, smaller defect)

Docker's proxy log shows 11 failing connections to a remote MCP server on three separate days, every one inside the last ~4.5 minutes before the stored OAuth token expires (the client answers Unauthorized at initialize, then client is closing, while docker mcp oauth ls still says authorized). A clean read follows a few minutes later. Forty startups inside such windows were clean, so it is intermittent. Separately, on 2026-10-08 the refresh woke 0.34 s before Docker's own 10 s threshold, did nothing, and tried again 30 s later, leaving the stored token hard-expired for about 20 s. A log line token refresh succeeded appeared on a no-op re-save, so it proves only that a pass finished. We have NOT proven the mechanism and are not claiming the refresh lacks single-flight (the proxy log shows one token exchange in the burst we first suspected).

What we ruled out

Display sleep and screen lock; keychain auto-lock (the login keychain reads no-timeout and unlocked, 15 ms reads); the Mac sleeping (held awake); a download or downgrade (4.94.0 is the newest release and wedges were seen on 4.76 and 4.78). A load of 14 dead OAuth providers was removed after the first jam and the jams continued with four live providers.

What we do not know

Why SecItemCopyMatching stops answering inside the backend. secd logs say Docker's helper binaries are treated as having an "invalid application signature or incorrect provisioning profile" (entitlement group.com.docker ignored; -34018 for docker-credential-*), but codesign --verify on the app passes and no SecurityAgent prompt was open. The new measurement says the fault is per-process rather than system-wide; we have not found what the stuck call is waiting on.

Offer

We can attach the full goroutine dumps (paths and account names removed) and a timeline of the seven episodes if useful.

Related

Platform

macOS

Version information

Docker Desktop 4.94.0 (newest release at the time of writing)
Docker Engine 29.8.2
Docker MCP Toolkit v0.44.1
macOS 26.6.2

Diagnostics ID

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions