Stop hook receives stale transcript data - race condition

Resolved 💬 2 comments Opened Dec 30, 2025 by em Closed Dec 30, 2025

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

  1. Configure a stop hook that checks the last assistant message for specific text patterns
  2. Have the assistant output text containing the pattern (e.g., "awaiting")
  3. The hook reads the transcript file but does NOT see the current message
  4. 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.readFileSync on the transcript_path

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗