Description
When shadowenv is used in subprocess environments where stderr is redirected through process substitutions (e.g., in Claude Code's shell integration or similar tools), hookbook's DEBUG trap generates stat: cannot statx errors. These errors occur because the trap attempts to stat transient pipe/socket file descriptors that are created by process substitution.
Root Cause
The __hookbook_debug_handler in sh/hookbook.sh:82-87 checks if stderr is redirected to /dev/null by comparing the major device number of /dev/fd/2 with that of /dev/null. When stderr is redirected through process substitution (e.g., exec 2> >(command)), /dev/fd/2 points to a transient pipe or socket file descriptor (e.g., /proc/XXXX/fd/pipe:[XXXXX] or /proc/XXXX/fd/socket:[XXXXX]).
The stat command fails when trying to check these transient descriptors because:
- They may be closed or unavailable by the time
stat runs (race condition)
- The path resolved by
readlink -f may not be statable
This results in error messages like:
stat: cannot statx '/proc/XXXX/fd/pipe:[XXXXX]': No such file or directory
Reproduction
Run the following test script:
#!/usr/bin/env bash
# Run a subprocess that redirects stderr through process substitution
{
# Redirect stderr through a process substitution
# This makes /dev/fd/2 point to a pipe/socket
exec 2> >(cat >&2)
# Source hookbook
source sh/hookbook.sh
# Add hook
test_func() { :; }
hookbook_add_hook test_func
# Run commands that trigger DEBUG trap
# Each one will try to stat /dev/fd/2 which is now a pipe
echo "Command 1" >&2
:
true
echo "Command 2" >&2
false || :
echo "Command 3" >&2
} 2>&1
Expected: Commands execute without errors
Actual: stat: cannot statx errors are printed to stderr
Environment
- Affects Linux systems (uses
stat -c "%t" and readlink -f)
- Occurs in subprocess environments with process substitution redirects
- Commonly encountered in tools like Claude Code that use socket-based IPC for shell integration
Description
When shadowenv is used in subprocess environments where stderr is redirected through process substitutions (e.g., in Claude Code's shell integration or similar tools), hookbook's DEBUG trap generates
stat: cannot statxerrors. These errors occur because the trap attempts to stat transient pipe/socket file descriptors that are created by process substitution.Root Cause
The
__hookbook_debug_handlerinsh/hookbook.sh:82-87checks if stderr is redirected to/dev/nullby comparing the major device number of/dev/fd/2with that of/dev/null. When stderr is redirected through process substitution (e.g.,exec 2> >(command)),/dev/fd/2points to a transient pipe or socket file descriptor (e.g.,/proc/XXXX/fd/pipe:[XXXXX]or/proc/XXXX/fd/socket:[XXXXX]).The
statcommand fails when trying to check these transient descriptors because:statruns (race condition)readlink -fmay not be statableThis results in error messages like:
Reproduction
Run the following test script:
Expected: Commands execute without errors
Actual:
stat: cannot statxerrors are printed to stderrEnvironment
stat -c "%t"andreadlink -f)