Skip to content

Commit 87b7a33

Browse files
committed
fix: Prevent file descriptor leaks in hook subprocess calls
**Problem**: EIO error (errno -5) on fd 17 when hooks executed due to improper file descriptor handling in subprocess invocations. **Root Cause**: Subprocesses (uuidgen, date, mnemosyne, jq) were inheriting file descriptors from parent process without proper stdin protection, causing read attempts on invalid/closed fds. **Solution**: Added stdin protection to all subprocess calls: - Commands that don't read stdin: Added `< /dev/null` (uuidgen, date, mnemosyne recall) - jq with `-n` flag: Added `< /dev/null` (doesn't read stdin) - jq reading files: Changed from `jq ... "$FILE"` to `jq ... < "$FILE"` (explicit stdin) - jq in pipes: No stdin redirect needed (pipe IS stdin) **Changes**: - .claude/hooks/session-start.sh: Protected uuidgen, mnemosyne recall, jq -n calls - .claude/hooks/post-tool-use.sh: Protected uuidgen, date, jq file reads - .claude/hooks/on-stop.sh: Changed jq file reads to use stdin redirect **Testing**: ✅ All hooks execute cleanly in silent mode (CC_HOOK_DEBUG=0) ✅ Debug output works correctly (CC_HOOK_DEBUG=1) ✅ Memory debt tracking functions properly ✅ No fd errors or warnings **Related**: Complements terminal corruption fixes (#eec1a33, #048f26d)
1 parent 048f26d commit 87b7a33

File tree

3 files changed

+11
-11
lines changed

3 files changed

+11
-11
lines changed

.claude/hooks/on-stop.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ if [ ! -f "$STATE_FILE" ]; then
1111
exit 0
1212
fi
1313

14-
DEBT=$(jq '.memory_debt' "$STATE_FILE" 2>/dev/null || echo "0")
15-
COUNT=$(jq '.memories_stored_count' "$STATE_FILE" 2>/dev/null || echo "0")
14+
DEBT=$(jq '.memory_debt' < "$STATE_FILE" 2>/dev/null || echo "0")
15+
COUNT=$(jq '.memories_stored_count' < "$STATE_FILE" 2>/dev/null || echo "0")
1616

1717
# Only show reminder if there's an issue AND debug mode enabled
1818
if [ "${CC_HOOK_DEBUG:-0}" = "1" ]; then

.claude/hooks/post-tool-use.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ STATE_FILE=".claude/memory-state.json"
1111
if [ ! -f "$STATE_FILE" ]; then
1212
cat > "$STATE_FILE" <<EOF
1313
{
14-
"session_id": "$(uuidgen)",
14+
"session_id": "$(uuidgen < /dev/null)",
1515
"memories_stored_count": 0,
1616
"last_memory_timestamp": null,
1717
"significant_events": [],
1818
"memory_debt": 0,
19-
"session_start": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
19+
"session_start": "$(date -u +%Y-%m-%dT%H:%M:%SZ < /dev/null)",
2020
"last_recall": null
2121
}
2222
EOF
@@ -26,12 +26,12 @@ fi
2626
case "$TOOL_USE" in
2727
Edit|Write|Bash*commit*)
2828
# Increment memory debt
29-
DEBT=$(jq '.memory_debt' "$STATE_FILE" 2>/dev/null || echo "0")
29+
DEBT=$(jq '.memory_debt' < "$STATE_FILE" 2>/dev/null || echo "0")
3030
DEBT=$((DEBT + 1))
3131

3232
# Update state
3333
TMP_FILE=$(mktemp)
34-
jq ".memory_debt = $DEBT | .significant_events += [{\"type\": \"$TOOL_USE\", \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\", \"has_memory\": false}]" "$STATE_FILE" > "$TMP_FILE"
34+
jq ".memory_debt = $DEBT | .significant_events += [{\"type\": \"$TOOL_USE\", \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ < /dev/null)\", \"has_memory\": false}]" < "$STATE_FILE" > "$TMP_FILE"
3535
mv "$TMP_FILE" "$STATE_FILE"
3636

3737
# If debt >= 3, inject urgent prompt (only visible in debug mode)
@@ -54,7 +54,7 @@ Store memories about your recent work using:
5454
5555
Recent unrecorded events:
5656
EOF
57-
jq -r '.significant_events[] | select(.has_memory == false) | " • \(.type) at \(.timestamp)"' "$STATE_FILE" | tail -5 >&2
57+
jq -r '.significant_events[] | select(.has_memory == false) | " • \(.type) at \(.timestamp)"' < "$STATE_FILE" | tail -5 >&2
5858

5959
cat >&2 <<EOF
6060

.claude/hooks/session-start.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ PROJECT_NAME="$(basename "$PROJECT_DIR")"
1818

1919
# Initialize memory state file
2020
STATE_FILE=".claude/memory-state.json"
21-
SESSION_ID=$(uuidgen)
21+
SESSION_ID=$(uuidgen < /dev/null)
2222

2323
cat > "$STATE_FILE" <<EOF
2424
{
@@ -58,7 +58,7 @@ MEMORIES=$("$MNEMOSYNE_BIN" recall \
5858
--namespace "$NAMESPACE" \
5959
--limit 10 \
6060
--min-importance 7 \
61-
--format json 2>/dev/null || echo '{"results": []}')
61+
--format json < /dev/null 2>/dev/null || echo '{"results": []}')
6262

6363
# Count memories
6464
MEMORY_COUNT=$(echo "$MEMORIES" | jq -r '.results | length' 2>/dev/null || echo "0")
@@ -101,7 +101,7 @@ $LINK_COUNT semantic connections across $MEMORY_COUNT memories
101101
"additionalContext": $context
102102
},
103103
"suppressOutput": true
104-
}'
104+
}' < /dev/null
105105
else
106106
# Optional debug output (only if CC_HOOK_DEBUG=1)
107107
if [ "${CC_HOOK_DEBUG:-0}" = "1" ]; then
@@ -130,5 +130,5 @@ Memory enforcement is active. Store memories to avoid blocking later."
130130
"additionalContext": $context
131131
},
132132
"suppressOutput": true
133-
}'
133+
}' < /dev/null
134134
fi

0 commit comments

Comments
 (0)