Skip to content

fix: prevent readStdin() from hanging indefinitely on Windows - #74

Open
myselfsiddharth wants to merge 1 commit into
supermemoryai:mainfrom
myselfsiddharth:fix/windows-stdin-hang
Open

fix: prevent readStdin() from hanging indefinitely on Windows#74
myselfsiddharth wants to merge 1 commit into
supermemoryai:mainfrom
myselfsiddharth:fix/windows-stdin-hang

Conversation

@myselfsiddharth

@myselfsiddharth myselfsiddharth commented Jul 6, 2026

Copy link
Copy Markdown

Summary

Fixes #25

On Windows, piped stdin never emits the end event for hook subprocesses spawned by Claude Code. readStdin() in src/lib/stdin.js waited for that event with no timeout, so every hook (SessionStart, UserPromptSubmit, PreToolUse, Stop) hung at its very first line and burned the full hook timeout before any authentication or API work began.

Changes to readStdin()

  • Eager parse on each chunk: hooks receive exactly one JSON object on stdin, so the promise now resolves as soon as the accumulated input parses as valid JSON. On Windows this means hooks get their input in milliseconds instead of hanging, with no reliance on the timeout at all.
  • 3 second timeout fallback: if no complete input ever arrives, it resolves with {} instead of hanging. All four hook callers already handle empty input gracefully (they fall back to process.cwd() and skip work that needs session_id or transcript_path).
  • Release stdin on settle: pause() plus unref() so the still-open pipe cannot keep the Node process alive after the promise settles, which is the underlying mechanism of the hang.

Unix behavior is unchanged: end still resolves normally, and malformed JSON with a closed stdin still rejects with the same error message as before.

Bundles in plugin/scripts/ were rebuilt with npm run build (the bundle diff churn is from esbuild output formatting; the only source change is src/lib/stdin.js).

Test plan

Verified on Windows 11 by spawning child processes against both the source module and the built bundles:

Scenario Result
Valid JSON, stdin closed (normal Unix path) resolved in ~10 ms
Valid JSON, stdin never closed (the Windows bug) resolved in ~10 ms
No input, stdin never closed resolved with {} after 3 s
Invalid JSON, stdin closed rejects with the same parse error as before
JSON split across chunks, stdin never closed resolved once the object completed
Built recall-hook.cjs with stdin held open process exited in ~200 ms with correct hook output

npx biome check and npm run build both pass.

  • Confirm hooks still behave normally on macOS and Linux

@vorflux

vorflux Bot commented Jul 6, 2026

Copy link
Copy Markdown

Vorflux skipped this auto review because this account has reached its Auto Review daily review limit (40/40). You can change this in Auto Review Settings: https://us1.vorflux.com/supermemory/settings?section=pull-requests

@ishaanxgupta

Copy link
Copy Markdown
Contributor

@myselfsiddharth can you resolve the conflicts?

On Windows, piped stdin never emits the 'end' event for hook
subprocesses spawned by Claude Code, so readStdin() waited forever
and every hook burned its full timeout before doing any work.

Fixes supermemoryai#25

Changes to readStdin():
- Parse eagerly on each data chunk: hooks receive a single JSON
  object, so resolve as soon as the accumulated input parses,
  without waiting for 'end' or any timeout
- Add a 3 second timeout fallback that resolves with an empty
  object when no complete input arrives; all callers already
  handle empty input gracefully
- Pause and unref stdin once settled so the open pipe cannot keep
  the process alive

Unix behavior is unchanged: 'end' still resolves normally and
malformed JSON with a closed stdin still rejects with the same
error message.

Bundles in plugin/scripts were rebuilt with npm run build.
@myselfsiddharth

Copy link
Copy Markdown
Author

@ishaanxgupta done — rebased onto main and conflicts are resolved.

All the conflicts were in the generated plugin/scripts/*.cjs bundles, not in source. main never touched src/lib/stdin.js, so I took main's bundles for every conflicted file and re-ran npm run build instead of hand-merging minified output.

That also shrinks the diff. The earlier bundle churn was from building with a different esbuild version; with the pinned one, main's committed bundles rebuild byte-identically, so the only bundles that change now are the four hooks that actually call readStdin():

before after
Source src/lib/stdin.js src/lib/stdin.js (unchanged)
Bundles all 8 context-hook, recall-hook, recall-approve, summary-hook

add-memory, save-project-memory, search-memory and status don't read stdin, so they're untouched.

Re-verified on macOS after the rebase — behavior is unchanged from the original test plan:

Scenario Result
Valid JSON, stdin closed (normal Unix path) resolved in 5 ms
Valid JSON, stdin never closed (the Windows bug) resolved in 3 ms, process exited
No input, stdin never closed resolved {} after 3.0 s
Invalid JSON, stdin closed rejects with the same parse error as before
JSON split across chunks, stdin never closed resolved once the object completed
Built recall-approve.cjs with stdin held open exited in 66 ms with correct hook output

npx biome check and npm test (5/5) both pass. GitHub now reports the PR as mergeable.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] readStdin() hangs indefinitely on Windows - no timeout on stdin end event

2 participants