Skip to content

Windows: web UI hangs — list_projects deadlocks the HTTP server (git via _popen handle inheritance) #798

Description

@Flipper1994

Windows: web UI hangs forever — list_projects deadlocks the HTTP server (git subprocess via _popen)

Summary

On Windows, with the graph UI enabled (--ui=true), the web UI never loads: the
page shell is served but the app stays blank. The cause is that the MCP tool
list_projects hangs forever and, because the UI HTTP server is
single-threaded, wedges the entire server — no further request is answered.

  • Affected: Windows, UI mode only (--ui=true / the web graph). Reproduced
    with the production binary built from main (cbm-with-ui).
  • Not affected: the plain MCP-stdio server (no UI) and the CLI — list_projects
    returns instantly there. That is why normal MCP usage never showed the problem.
  • Requires: git-for-Windows (the MSYS2/Cygwin build) on PATH, which is the
    standard Windows git.

Root cause (confirmed with a debugger)

list_projects resolves git context per project:
add_git_context_jsoncbm_git_context_resolvegit_capturecbm_popen
(src/git/git_context.c). On Windows cbm_popen is the CRT _popen, which spawns
the child with CreateProcess(bInheritHandles = TRUE) and therefore leaks every
inheritable handle
the server holds into the git child — including the
Winsock/AFD helper handles that only exist once the HTTP server has called
WSAStartup/socket().

git-for-Windows (MSYS2/Cygwin runtime) classifies every inherited handle on
startup by calling NtQueryObject on it. NtQueryObject deadlocks on an
inherited socket/AFD handle. So git never reaches rev-parse, never writes output,
and the server thread blocks forever in fgets on git's stdout pipe.

Three-thread deadlock captured with gdb on the hung process:

# UI request thread — waiting for git output that never comes
ntdll!ZwReadFile -> ReadFile -> msvcrt!fgets
  -> git_capture() (git_context.c)
  -> cbm_git_context_resolve -> add_git_context_json -> handle_list_projects
  -> cbm_mcp_server_handle -> cbm_http_server_run -> http_thread

# the git.exe child — stuck classifying an inherited handle
ntdll!ZwQueryObject   (NtQueryObject)

# server main thread — the blocking read on the MCP stdin pipe git inherited/queries
ntdll!ZwReadFile -> ... -> cbm_getline -> cbm_mcp_server_run

Why UI-only: the plain stdio server and the CLI have no listening/AFD socket
handles, so git inherits nothing that trips NtQueryObject.

Reproduction

  1. On Windows, build with the UI: make -f Makefile.cbm cbm-with-ui.
  2. Index any two git repos so list_projects returns >1 project.
  3. Run codebase-memory-mcp --ui=true --port=9749 and open the UI (or just
    POST /rpc {"method":"tools/call","params":{"name":"list_projects"}}).
  4. The request never returns; the whole UI server stops responding. tasklist
    shows a cmd /c git ... rev-parse child that never exits.

Fix

Spawn git so it inherits only the stdout pipe. See the PR: cbm_popen on
Windows is reimplemented with CreateProcess + STARTUPINFOEX and an explicit
PROC_THREAD_ATTRIBUTE_HANDLE_LIST (stdout write-end + a NUL handle for
stdin/stderr). Nothing else crosses into git → no foreign handle to deadlock on.
The POSIX path is unchanged (popen already sets O_CLOEXEC).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.stability/performanceServer crashes, OOM, hangs, high CPU/memoryux/behaviorDisplay bugs, docs, adoption UXwindowsWindows-specific issues

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions