# How to Debug Hook Failures Using Log Files in $TMPDIR/letta-claude-sync-$UID

> Debug hook failures in letta-claude-sync-$UID log files. Discover specific error details for each hook in dedicated session and message log files to quickly resolve issues.

- Repository: [Letta/claude-subconscious](https://github.com/letta-ai/claude-subconscious)
- Tags: how-to-guide
- Published: 2026-03-26

---

**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`](https://github.com/letta-ai/claude-subconscious/blob/main/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 `/tmp` on macOS and Linux)
- **`process.getuid()`** or `process.pid`: Appends the Unix user ID, falling back to the process ID if `getuid` is 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`](https://github.com/letta-ai/claude-subconscious/blob/main/scripts/session_start.ts)): Writes diagnostic messages to `session_start.log` via the `log()` function after resolving the path through `getTempStateDir()` 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`](https://github.com/letta-ai/claude-subconscious/blob/main/scripts/send_messages_to_letta.ts)): Defines `LOG_FILE` at lines 48-51 to create `send_messages.log` for 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`](https://github.com/letta-ai/claude-subconscious/blob/main/scripts/pretool_sync.ts)): Outputs execution details to `pretool_sync.log` using the identical logging pattern
- **UserPromptSubmit** ([`scripts/sync_letta_memory.ts`](https://github.com/letta-ai/claude-subconscious/blob/main/scripts/sync_letta_memory.ts)): While lacking an explicit file logger, this hook prints debug messages to **stderr** when `LETTA_DEBUG=1` is 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:

```bash

# 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:

```bash

# 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`](https://github.com/letta-ai/claude-subconscious/blob/main/sync_letta_memory.ts)) to emit detailed diagnostics:

```bash
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:

```bash
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 API
- **`Error: Letta API error (401)…`** — Authentication failure; verify `LETTA_API_KEY` is exported correctly
- **`Failed to load state…`** — Corrupted or permission-denied state file in `.letta/claude/`
- **`Mode: off`** — Intentional exit; the hook disabled itself because `LETTA_MODE=off` is set

## Advanced Debugging Code Examples

Extract the conversation URL from SessionStart logs to verify API connectivity:

```bash
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:

```bash
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:

```bash
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 in [`scripts/conversation_utils.ts`](https://github.com/letta-ai/claude-subconscious/blob/main/scripts/conversation_utils.ts) via `getTempStateDir()`
- **Four dedicated log files** exist: `session_start.log`, `send_messages.log`, `pretool_sync.log`, and `sync_letta_memory.log` (stderr capture)
- **Enable debug mode** by setting `LETTA_DEBUG=1` to expose detailed memory-sync diagnostics
- **Common fixes** involve checking `LETTA_API_KEY` for 401 errors, verifying directory permissions, and clearing stale state entries
- **Real-time monitoring** with `tail -f` captures 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`](https://github.com/letta-ai/claude-subconscious/blob/main/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`](https://github.com/letta-ai/claude-subconscious/blob/main/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.