Skip to content

SessionStart/Stop hooks time out frequently on Windows — eager CLI imports + disabled bytecode cache make every invocation pay full interpreter+import cost #168

Description

@ppuri2

Summary

The SessionStart and Stop hooks (scripts/claude-obsidian.py hook session-start / hook stop, 5s timeout in hooks/hooks.json) time out on a large fraction of runs in real usage, even though the hook logic itself (hook_adapter.session_start_context / stop_status) is trivial — a couple of bounded file reads.

Evidence

Scanned my own Claude Code session transcripts over 13 days / 48 sessions:

Hook Runs Timed out
SessionStart:startup 20 19 (95%), up to ~21s
SessionStart:resume 3 3 (100%), up to ~11s
Stop 4 3 (75%), up to ~10s

Running the exact hook command in isolation (no concurrent load) takes a consistent ~0.9-1.0s — under the 5s timeout, but still slow for what the hook actually does. Notably, user+sys CPU time for that run is only ~0.02-0.08s of the ~1s wall clock — the process spends nearly all its time waiting on something external (process launch / file-read interception), not computing.

Suspected causes

  1. sys.dont_write_bytecode = True in scripts/claude-obsidian.py disables Python's .pyc cache, so every hook invocation recompiles all imported modules from source instead of reusing a cache after the first run.
  2. claude_obsidian/cli.py eagerly imports ~13 submodules at module level (capture, checkpoint, contracts, extensions, gates, ledgers, lint_engine, mode_config, package_validation, release, transaction, vault_ops, plus hook_adapter) regardless of which subcommand is invoked. The hook session-start/hook stop path only needs hook_adapter — the other ~12 are dead weight on this path.
  3. On Windows specifically, python3 commonly resolves to the Store app-execution-alias stub (...\AppData\Local\Microsoft\WindowsApps\python3.exe) ahead of a real interpreter on PATH, which adds its own launch overhead before Python even starts. Confirmed this resolution order on my machine via both where python3 and Get-Command python3 -All.

None of these alone fully explains the 5-21s real-world timeouts (my isolated runs stayed under 1.5s), so I suspect contention: at actual session start, this hook runs concurrently with Claude Code connecting MCP servers and loading other plugins/hooks. If antivirus real-time scanning is involved (plausible given the near-zero CPU time relative to wall time, and that .pyc caching is disabled so every file gets freshly read every time), that contention would compound significantly.

Suggested fixes

  • Remove sys.dont_write_bytecode = True (or scope it to only the commands that need it) so repeated invocations benefit from Python's normal bytecode cache.
  • Make the imports in cli.py lazy per-subcommand, or give the hook subcommand its own thin entry point that only imports hook_adapter, so hook session-start/hook stop don't pull in lint/transaction/release/vault_ops machinery they never touch.
  • Consider raising the default hook timeout, or documenting that Windows users may want to point hooks.json's command at a specific interpreter path rather than bare python3, to avoid the Store alias stub.

Environment

  • OS: Windows 11 Pro
  • Claude Code: 2.1.233 (native install)
  • Plugin version: claude-obsidian 2.1.0
  • Python: 3.14.7 (multiple interpreters on PATH; python3 resolves to the WindowsApps alias first)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions