Stop hook receives stale transcript data - race condition
Bug Description
The stop hook is invoked BEFORE the transcript file write is fully flushed/visible to the hook process. This causes the hook to read stale data, missing the current assistant message.
Reproduction
- Configure a stop hook that checks the last assistant message for specific text patterns
- Have the assistant output text containing the pattern (e.g., "awaiting")
- The hook reads the transcript file but does NOT see the current message
- On the NEXT assistant turn, the hook finally sees the previous message
Evidence
Tested by deliberately outputting "awaiting" in a message:
- Message 1: Wrote "I am awaiting the hook to catch this message"
- Hook result:
✓ VIOLATION_WORK_REFUSAL: None detected
- Message 2: Quoted the word "awaiting" again
- Hook result:
✗ Work refusal detected: 'awaiting'
The hook caught "awaiting" in message 2 because by then message 1 was finally flushed to disk. The hook is always one message behind.
Root Cause
Claude Code appears to spawn the hook process before the transcript file I/O is complete. The hook's readFileSync call reads whatever was flushed at that moment, which excludes the current assistant message.
Expected Behavior
The stop hook should receive a transcript that includes the complete current assistant message that triggered the hook.
Suggested Fix
Ensure fsync/flush of the transcript file completes BEFORE invoking the stop hook process.
Environment
- Claude Code version: 2.0.76
- Platform: Linux
- Hook implementation: Node.js using
fs.readFileSyncon thetranscript_path
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗