openai / openai/codex-plugin-cc

SessionEnd hook can hit 5s timeout on Windows due to shelling out to git

Open
#403 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
33.3k
Forks
2.3k
PR merge metrics
No merged PRs in 30d

Description

Summary

On Windows, the codex@openai-codex Claude plugin SessionEnd hook can exceed its configured 5s timeout and Claude reports:

SessionEnd hook [node "${CLAUDE_PLUGIN_ROOT}/scripts/session-lifecycle-hook.mjs" SessionEnd] failed: Hook cancelled

I reproduced this on plugin 1.0.4. In the no-broker teardown path, session-lifecycle-hook.mjs still calls:

handleSessionEnd -> cleanupSessionJobs -> resolveWorkspaceRoot -> ensureGitRepository

resolveWorkspaceRoot delegates to git rev-parse --show-toplevel through runCommand, which uses spawnSync(..., { shell: true }) on Windows. That emits Node DEP0190 and adds shell/git process overhead to every session close.

Local evidence

With realistic SessionEnd hook JSON (hook_event_name, session_id, and cwd) and CLAUDE_PLUGIN_DATA set to the real plugin data dir, the pre-patch hook showed the positive-control DEP0190 on every run:

{
  "prePatchElapsedMs": [6205, 4796, 8192],
  "prePatchDep0190": [true, true, true]
}

After replacing resolveWorkspaceRoot with a pure fs upward .git walk, the same hook harness completed quickly and emitted no DEP0190:

{
  "postPatchElapsedMs": [103, 103, 107],
  "postPatchDep0190": [false, false, false]
}

I also compared the full resolveStateDir(cwd) output before and after the patch across 12 cases:

  • main checkout and nested subdir
  • linked worktree and nested subdir
  • junction-accessed worktree and nested subdir
  • CLAUDE_PLUGIN_DATA set and unset

All state-dir outputs were byte-identical.

Proposed patch

  1. In scripts/lib/workspace.mjs, replace resolveWorkspaceRoot(cwd) with a pure fs upward search for a .git file or directory. Return fs.realpathSync.native() for the found root so both the state-dir hash and raw basename slug stay aligned with git rev-parse --show-toplevel.

  2. In scripts/lib/broker-lifecycle.mjs, add a local timeout to sendBrokerShutdown(endpoint). A live-but-unresponsive broker should not be able to hold the SessionEnd hook past the configured timeout. A 1500ms socket timeout resolved an intentionally unresponsive named-pipe test in 1516ms.

Notes

  • This does not require increasing the hook timeout. Increasing it hides the symptom but also makes Claude block longer on close.
  • The broker timeout is not the reproduced no-broker cause; it closes a related live-broker teardown risk.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with scripts/lib/workspace.mjs and scripts/lib/broker-lifecycle.mjs, then run the realistic SessionEnd hook harness described in the issue on Windows. Done means workspace resolution no longer shells out to git, an unresponsive broker is bounded by the local timeout, state-dir outputs remain identical, and the hook finishes without DEP0190.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
devtools, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.