How to Debug Hook Failures Using Log Files in $TMPDIR/letta-claude-sync-$UID
When Claude Code reports a "hook error" in Claude Subconscious, examine the per-user log directory at $TMPDIR/letta-claude-sync-$UID/ where each hook writes to dedicated files (e.g., session_start.log, send_messages.log) containing the exact error details obscured by the main interface.
The letta-ai/claude-subconscious repository enhances Claude Code with four background TypeScript hooks—SessionStart, UserPromptSubmit, PreToolUse, and Stop—that execute as Node.js scripts from the scripts/ directory. Because these processes run asynchronously outside the main Claude Code interface, their diagnostic output streams to temporary log files rather than the terminal. Learning to debug hook failures using log files in $TMPDIR/letta-claude-sync-$UID/ enables you to resolve API authentication errors, state corruption, and runtime exceptions that cause silent failures.
Understanding the Log Directory Structure
How the Path is Constructed
The temporary directory path is built dynamically in scripts/conversation_utils.ts by the getTempStateDir() function at lines 60-63【/cache/repos/github.com/letta-ai/claude-subconscious/main/scripts/conversation_utils.ts#L60-L63】. This utility combines:
os.tmpdir(): Resolves to the system temporary directory (typically/tmpon macOS and Linux)process.getuid()orprocess.pid: Appends the Unix user ID, falling back to the process ID ifgetuidis unavailable
The resulting path follows the pattern $TMPDIR/letta-claude-sync-$UID/, ensuring per-user isolation of log data.
Log Files by Hook Type
Each hook script creates and writes to a specific log file within this directory:
- SessionStart (
scripts/session_start.ts): Writes diagnostic messages tosession_start.logvia thelog()function after resolving the path throughgetTempStateDir()at lines 71-79【/cache/repos/github.com/letta-ai/claude-subconscious/main/scripts/session_start.ts#L71-L79】 - Stop (
scripts/send_messages_to_letta.ts): DefinesLOG_FILEat lines 48-51 to createsend_messages.logfor capturing shutdown synchronization events【/cache/repos/github.com/letta-ai/claude-subconscious/main/scripts/send_messages_to_letta.ts#L48-L51】 - PreToolUse (
scripts/pretool_sync.ts): Outputs execution details topretool_sync.logusing the identical logging pattern - UserPromptSubmit (
scripts/sync_letta_memory.ts): While lacking an explicit file logger, this hook prints debug messages to stderr whenLETTA_DEBUG=1is enabled; Claude Code captures this stream into the same temporary directory
Locating Your Log Files Before Debugging
Determine the exact log directory for your session using environment variables and the id command:
# Resolve the complete log directory path
LOG_DIR="${TMPDIR:-/tmp}/letta-claude-sync-$(id -u)"
echo "$LOG_DIR"
# List available log files
ls -la "$LOG_DIR"
If the directory does not exist, the hooks have not yet initialized or lack permissions to write to the temporary filesystem.
Debugging Hook Failures Step by Step
1. Inspect the Relevant Log File
Match the failing hook to its log file and examine the tail for recent errors:
# For SessionStart failures
tail -n 40 "${TMPDIR:-/tmp}/letta-claude-sync-$(id -u)/session_start.log"
# For Stop hook failures
tail -n 40 "${TMPDIR:-/tmp}/letta-claude-sync-$(id -u)/send_messages.log"
2. Enable Verbose Debug Output
Set the LETTA_DEBUG environment variable before starting Claude Code to force the memory-sync hook (sync_letta_memory.ts) to emit detailed diagnostics:
export LETTA_DEBUG=1
export LETTA_MODE=whisper # or full/off as required
# Launch Claude Code normally
Debug messages now stream to stderr and are captured in the temporary directory alongside other hook logs.
3. Watch Logs in Real-Time
Reproduce the failure while monitoring the log file to capture transient errors:
tail -f "${TMPDIR:-/tmp}/letta-claude-sync-$(id -u)/session_start.log"
4. Interpret Common Error Patterns
Search for these specific strings to identify root causes:
Creating new conversation…— The hook is successfully contacting the Letta APIError: Letta API error (401)…— Authentication failure; verifyLETTA_API_KEYis exported correctlyFailed to load state…— Corrupted or permission-denied state file in.letta/claude/Mode: off— Intentional exit; the hook disabled itself becauseLETTA_MODE=offis set
Advanced Debugging Code Examples
Extract the conversation URL from SessionStart logs to verify API connectivity:
LOG_DIR="${TMPDIR:-/tmp}/letta-claude-sync-$(id -u)"
tail -n 20 "$LOG_DIR/session_start.log" | grep -i conversation
Force a fresh conversation when the state map becomes stale by removing the specific session entry:
SESSION_ID=$(jq -r '.session_id' < "$HOME/.claude/session.json")
LOG_FILE="${TMPDIR:-/tmp}/letta-claude-sync-$(id -u)/session_start.log"
sed -i.bak "/$SESSION_ID/d" "$LOG_FILE"
Stream debug output from the memory synchronization hook continuously:
export LETTA_DEBUG=1
tail -f "${TMPDIR:-/tmp}/letta-claude-sync-$(id -u)/sync_letta_memory.log"
Summary
- Hook logs reside in
$TMPDIR/letta-claude-sync-$UID/as defined inscripts/conversation_utils.tsviagetTempStateDir() - Four dedicated log files exist:
session_start.log,send_messages.log,pretool_sync.log, andsync_letta_memory.log(stderr capture) - Enable debug mode by setting
LETTA_DEBUG=1to expose detailed memory-sync diagnostics - Common fixes involve checking
LETTA_API_KEYfor 401 errors, verifying directory permissions, and clearing stale state entries - Real-time monitoring with
tail -fcaptures errors as they occur during hook execution
Frequently Asked Questions
What does the $UID suffix represent in the log directory path?
The $UID suffix represents the Unix user ID retrieved via process.getuid(), or the process ID if the function is unavailable, as implemented in scripts/conversation_utils.ts【/cache/repos/github.com/letta-ai/claude-subconscious/main/scripts/conversation_utils.ts#L60-L63】. This ensures separate log directories for each system user when multiple accounts run Claude Code on the same machine.
Why are my log files empty after a hook failure?
Empty logs indicate either the hook process crashed before initializing the logger, Claude Code sandboxing prevented file system access (common on macOS with strict privacy settings), or the temporary directory is not writable. Verify permissions with ls -ld "$TMPDIR" and ensure the letta-claude-sync-$UID directory is owned by your user.
How do I troubleshoot "Letta API error (401)" messages in the logs?
A 401 error signifies authentication failure against the Letta API. Verify that the LETTA_API_KEY environment variable is exported in your shell profile and accessible to the Claude Code process. The session_start.log file captures the full error response from the API, which often includes additional context about missing or invalid credentials.
Which log file corresponds to the memory synchronization hook?
The UserPromptSubmit hook (memory synchronization) writes to sync_letta_memory.log only when LETTA_DEBUG=1 is enabled, as the scripts/sync_letta_memory.ts file outputs to stderr rather than a dedicated file logger. Without debug mode enabled, this hook produces minimal output, so set the environment variable before starting Claude Code to capture diagnostic data.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →