Troubleshooting Guide
User Section
These are the most common issues reported by dashboard users.
Dashboard shows no events
Symptoms: The dashboard loads but the timeline is empty. The connection dot may be green (connected) but no event cards appear.
Checklist:
-
Verify hook installation. Claude Code must have
send-event.jsregistered for all 24 hook events in~/.claude/settings.json:{ "hooks": { "PreToolUse": [{ "type": "command", "command": "node /path/to/send-event.js", "matcher": "" }], "PostToolUse": [{ "type": "command", "command": "node /path/to/send-event.js", "matcher": "" }], "PostToolUseFailure": [{ "type": "command", "command": "node /path/to/send-event.js", "matcher": "" }], "Notification": [{ "type": "command", "command": "node /path/to/send-event.js", "matcher": "" }], "PreCompact": [{ "type": "command", "command": "node /path/to/send-event.js", "matcher": "" }], "PermissionRequest": [{ "type": "command", "command": "node /path/to/send-event.js", "matcher": "" }], "Elicitation": [{ "type": "command", "command": "node /path/to/send-event.js", "matcher": "" }], "PermissionDenied": [{ "type": "command", "command": "node /path/to/send-event.js", "matcher": "" }], "UserPromptSubmit": [{ "type": "command", "command": "node /path/to/send-event.js" }], "Stop": [{ "type": "command", "command": "node /path/to/send-event.js" }], "SessionStart": [{ "type": "command", "command": "node /path/to/send-event.js" }], "SessionEnd": [{ "type": "command", "command": "node /path/to/send-event.js" }], "SubagentStart": [{ "type": "command", "command": "node /path/to/send-event.js" }], "SubagentStop": [{ "type": "command", "command": "node /path/to/send-event.js" }], "TaskCompleted": [{ "type": "command", "command": "node /path/to/send-event.js" }], "TeammateIdle": [{ "type": "command", "command": "node /path/to/send-event.js" }], "StopFailure": [{ "type": "command", "command": "node /path/to/send-event.js" }], "PostCompact": [{ "type": "command", "command": "node /path/to/send-event.js" }], "InstructionsLoaded": [{ "type": "command", "command": "node /path/to/send-event.js" }], "ConfigChange": [{ "type": "command", "command": "node /path/to/send-event.js" }], "CwdChanged": [{ "type": "command", "command": "node /path/to/send-event.js" }], "FileChanged": [{ "type": "command", "command": "node /path/to/send-event.js" }], "TaskCreated": [{ "type": "command", "command": "node /path/to/send-event.js" }], "ElicitationResult": [{ "type": "command", "command": "node /path/to/send-event.js" }] } }Run the appropriate setup script to install hooks:
setup.ps1(Windows),setup-wsl.sh(WSL),setup-macos.sh(macOS), orsetup-linux.sh(Linux). -
Verify the server is running. From any terminal:
curl http://localhost:3131/healthExpected response:
{"status":"ok"}. If the command fails, the server is not running. - Check port 3131. Ensure no firewall rule blocks localhost:3131. On WSL2, verify that localhost forwarding is working (Windows should forward WSL localhost automatically on recent builds).
- Start a Claude Code session. Events only appear when Claude Code is actively running with hooks installed. Start a conversation and send a prompt — you should see events within 1–2 seconds.
WebSocket keeps disconnecting
Symptoms: The connection dot flashes between green and red. Events appear intermittently or the dashboard shows a reconnecting message.
Possible causes:
- Firewall or proxy interference. Some corporate firewalls or browser extensions terminate idle WebSocket connections. Try adding an exception for localhost:3131.
-
WSL2 localhost forwarding. On older Windows builds, WSL2 localhost
forwarding can be unreliable. Check that
curl http://localhost:3131/healthworks from within WSL. If not, try using the WSL gateway IP instead. - Browser tab throttling. Background tabs in Chrome/Edge are aggressively throttled after 5 minutes, which can cause WebSocket timeouts. Keep the dashboard tab in the foreground or pin it to prevent throttling.
- Server crash. Check the server terminal for error output. The most common crash cause is a corrupted SQLite database — see “Server won’t start” below.
Token costs show $0.00
Symptoms: Session cost chips in the nav bar and session headers show $0.00 even though Claude Code is being used.
Requirements for cost tracking:
-
Real-time costs (from hook payloads) require that Claude Code sends
usagedata in Stop events. This is standard behavior and should work automatically. If costs are $0.00, check that Stop events are appearing in the timeline. - Verified costs (from Anthropic Usage API) require an Admin API key, not a regular inference key. Configure it in Settings as the “Anthropic Admin API Key”. The Usage API is polled on a configurable interval (default: 20 minutes).
-
Polling interval. After entering the Admin API key, wait up to 20 minutes
for the first poll. You can adjust the interval in Settings
(
usage_api_poll_interval_min).
Security tab is empty
Symptoms: The Security tab shows no events or chains.
This is normal if no security-relevant events have occurred. The Security tab only shows:
- Events classified by the regex engine (Bash commands with network calls, credential access, code injection patterns, etc.)
-
LLM-analyzed security chains (requires
security_llm_enabledsetting to betrueand an Anthropic API key configured) -
Process spawn events from the process monitor (requires
process_monitor_enabledto betrue)
Run a Claude Code session that involves Bash commands with network activity, file writes to sensitive paths, or credential operations to see security events appear.
Insights show “pending” forever
Symptoms: The Insights tab shows session cards stuck in a “pending” or “analyzing” state that never complete.
Requirements:
- An Anthropic Inference API key must be configured in Settings.
- The
insights_enabledsetting must betrue(it is by default). -
The configured model must be available on your API key. Default:
claude-haiku-4-5-20251001.
Debugging:
- Check the server terminal for LLM API errors (rate limits, authentication failures, model not found).
- Verify the API key works: in Settings, check that the key shows as “configured” (not empty).
- Insights require at least 1 UserPromptSubmit event in the session.
Server won’t start
Symptoms: npm start or node start.js fails with
an error.
Common causes:
-
Port 3131 already in use. Another process is listening on port 3131.
Either stop it or set a different port:
PORT=3132 npm start -
better-sqlite3 native binary mismatch. If you see
ERR_DLOPEN_FAILEDor similar,start.jsshould auto-detect and rebuild. If it fails, manually rebuild:cd server && npm rebuild better-sqlite3 -
SQLite database corruption. If you see
SQLITE_CORRUPTorSQLITE_NOTADB, the database file may be damaged. Renamedata/agent-recon.db(and any-wal/-shmfiles) and restart — a fresh database will be created automatically. -
Node.js version. Agent Recon requires Node.js 22+. Check with
node --version.
Events appear duplicated
Symptoms: The same event appears twice in the timeline.
The dedup system uses a fingerprint of
session_id|agent_id|hook_event_name|tool_use_id|tool_name|prompt[:40] with a
2-second window. Duplicate events within this window are automatically suppressed.
Events that appear duplicated may be legitimately distinct:
-
Rapid identical tool calls (e.g., two
Readcalls to the same file within 2 seconds with different tool_use_ids) - Events from different agent_ids (main agent vs. subagent)
If true duplicates persist, verify that only one hook command is registered per event type
in settings.json. When upgrading from v1.0.x, ensure no legacy
send-event.py entries remain alongside the new send-event.js
entries — the installer's upgrade flow strips them, but manual config files may still
have both.
Windows-Specific Issues
node not found on Git Bash PATH (nvm-windows / Volta / fnm):
Claude Code on Windows runs hook commands through Git Bash, which sometimes doesn't share
PATH with the shell that installed Agent Recon. The installer probes for node --version
at install time and falls back to the absolute path of the current Node runtime when the
probe fails. If you switch Node versions after installing Agent Recon, re-run
agent-recon install (or setup.ps1) to refresh the hook command.
ExecutionPolicy blocks npm scripts:
The default Restricted policy prevents npm.ps1 from running. Fix:
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Node.js blocked by Windows Firewall:
Windows Defender blocks inbound connections to node.exe on dynamic ports. Allow
access for private networks when prompted, or create a manual firewall rule in
Windows Security → Firewall & network protection → Allow an app through
firewall.
PATH not updated after installing prerequisites: Tools installed via winget or standalone installers add to PATH, but existing terminal sessions may not see the change. Close and reopen your terminal, or start a new PowerShell window.
Installation order matters:
Install Claude Code first and run it at least once (to create
~/.claude/settings.json). Then install Agent Recon. The installer merges hooks
into the existing settings file.
EACCES on global install
If sudo npm install -g agent-recon was used and the server crashes with:
Error: EACCES: permission denied, mkdir '...data'
The data directory resolved to a root-owned location
(/usr/lib/node_modules/agent-recon/data/). As of v1.0.3, the server uses
platform-specific user-writable directories:
| Platform | Default path |
|---|---|
| Linux / WSL | ~/.config/agent-recon/data/ |
| macOS | ~/Library/Application Support/agent-recon/data/ |
| Windows | %APPDATA%\agent-recon\data\ |
Upgrade to v1.0.3+ to resolve. As a workaround, set AGENT_RECON_DATA_DIR:
AGENT_RECON_DATA_DIR=~/.config/agent-recon/data agent-recon start
Credential storage on headless Linux / SSH
On headless Linux servers or SSH sessions, libsecret requires a D-Bus session and
unlocked keyring, which are typically unavailable. The server falls back to PBKDF2 file-based
encryption automatically.
If you see credential-related errors, set a master key explicitly:
export AGENT_RECON_MASTER_KEY=$(openssl rand -hex 32)
agent-recon start
Store the key securely — it is needed to decrypt any previously encrypted API keys.
Session labels show hex instead of project name
Symptoms: Session labels display as session:a3f7 instead of
myproject:a3f7.
Session labels are generated by resolving the project name in this priority order:
- Git remote repository name (from
git remote get-url originin the session’s cwd) project_namefield from the first event payload- Last component of
cwdfrom the first event payload - Fallback:
"session"
If labels show session:xxxx, it means the working directory is not a git
repository or the git remote is not configured. This is cosmetic only and does not affect
functionality.
Developer / Support Section
For maintainers and support engineers debugging deeper issues.
Diagnostic Endpoints
The server exposes several diagnostic endpoints (no authentication, localhost only):
| Endpoint | Description |
|---|---|
GET /health | Returns {"status":"ok"} when server is running |
GET /diag | Uptime, memory usage, event counts by env, session count, cache size |
GET /api/db/stats | Database statistics: event count, session count, DB file size, schema version |
GET /api/settings | All settings (sensitive values redacted to {configured, prefix}) |
GET /api/version | Server version from package.json |
GET /api/tokens | Per-session token summaries |
GET /api/tokens/period | Today and month cost aggregates |
GET /api/license | Current license tier and feature availability |
Database Inspection
Direct SQLite inspection for debugging:
# Open the database
sqlite3 data/agent-recon.db
# Check schema version
PRAGMA user_version;
# List tables
.tables
# View full schema for a table
.schema events
# Recent events
SELECT id, session_id, hook_event, tool_name, received_at
FROM events ORDER BY received_at DESC LIMIT 20;
# Session labels
SELECT session_id, session_label, history_status
FROM sessions ORDER BY last_seen DESC LIMIT 10;
# Settings (with encrypted values masked)
SELECT key,
CASE WHEN value LIKE '$ENC$%' THEN '<encrypted>'
ELSE substr(value, 1, 50)
END AS value
FROM settings;
# Security chains
SELECT chain_id, session_id, risk_level, is_final, chain_type
FROM security_chains ORDER BY created_at DESC LIMIT 10;
# LLM analysis costs
SELECT module, model, SUM(input_tokens) as total_in,
SUM(output_tokens) as total_out, SUM(cost_usd) as total_cost
FROM llm_analysis_usage GROUP BY module, model;
Hook Verification
Verify that hooks are correctly installed and forwarding events:
-
Check settings.json:
node -e "console.log(JSON.stringify(JSON.parse(require('fs').readFileSync(require('os').homedir() + '/.claude/settings.json','utf8')), null, 2))"Confirm all 24 hook events are listed with the correct path to
send-event.js. -
Test the hook script manually:
echo '{"session_id":"test","hook_event_name":"UserPromptSubmit","prompt":"hello"}' | \ node ~/.claude/hooks/send-event.jsCheck the server terminal for the received event.
-
Verify hook script matches source:
sha256sum ~/.claude/hooks/send-event.js sha256sum /path/to/agent-recon/.claude/hooks/send-event.jsIf hashes differ, re-run the setup script.
Common Development Issues
NTFS path performance for SQLite
SQLite on NTFS/DrvFs (Windows drives mounted in WSL) is significantly slower than on ext4.
Additionally, WAL mode is not supported on DrvFs because mmap is unavailable.
start.js detects this and automatically migrates the database to a WSL-native
path.
For manual override, set DB_PATH to a WSL-native location:
DB_PATH=/home/user/.agent-recon/agent-recon.db npm start
LF line endings required
The Node.js hook script (send-event.js) and bash setup scripts
(.sh) must have LF line endings. CRLF breaks the
#!/usr/bin/env node shebang line on Unix and bash
set -euo pipefail. If hooks fail silently after editing on Windows, check line
endings:
file ~/.claude/hooks/send-event.js
# Should show: Node.js script executable, ASCII text
# Bad: Node.js script executable, ASCII text, with CRLF line terminators
build.js function-form replacement
The build system uses replace('{{JS}}', () => js) with a function argument,
not a string. This is because the JavaScript source contains $&,
$', and similar sequences that would be interpreted as replacement patterns
with string form. Never change this — it will cause silent corruption of the built SPA.
start.js binary swap for cross-platform development
When switching between Windows/WSL/macOS terminals, start.js automatically
detects better-sqlite3 binary mismatches and triggers
npm rebuild. The old binary is renamed (not deleted) because NTFS may lock the
file. This means you may see files like better_sqlite3.node.win32 or
better_sqlite3.node.linux in the build directory — these are safe to
ignore.
Debug Mode
Enable verbose logging by setting environment variables before starting the server:
# Show all DB queries
DEBUG=db npm start
# Show LLM API calls
DEBUG=llm npm start
# Show WebSocket message details
DEBUG=ws npm start
# Multiple debug channels
DEBUG=db,llm,ws npm start
Log Locations
Agent Recon does not write log files by default. All output goes to stdout/stderr of the server process:
-
Server logs: The terminal window where
npm startwas executed. On Windows, this is typically a PowerShell window that stays open. - Frontend logs: Browser developer console (F12). The frontend logs WebSocket connection state, event processing, and filter changes.
- Hook script errors: Silently dropped by default (hooks must not block Claude Code). To debug hook issues, run the hook script manually as shown in Hook Verification above.
Process Monitor Debugging
The process monitor (process-monitor.js) uses different mechanisms per platform:
-
Linux/WSL: Polls
/procevery 500ms. Check that/procis accessible and readable. -
Windows: Polls via PowerShell
Get-WmiObjectevery 2s. Requires PowerShell to be available in PATH.
If the process monitor is causing issues, disable it in Settings
(process_monitor_enabled = false).
OTEL Export Issues
If events are not reaching your OTEL collector:
- Verify
otel_enabledis'true'in Settings. - Check the
otel_endpointURL is correct (default:http://localhost:4318). - For authenticated collectors, verify
otel_auth_typeand credentials are set correctly. - Check the server terminal for OTLP export errors.
- Verify the collector accepts OTLP/HTTP (JSON) on the configured endpoint.
TLS / HTTPS Issues
mkcert not found
The server reports “mkcert is not installed” when attempting TLS setup. Install
mkcert for your platform (brew install mkcert on macOS,
choco install mkcert on Windows, apt install mkcert on
Linux/WSL), then run mkcert -install to create and trust the root CA.
Elevation required
mkcert -install needs admin/sudo privileges to install the root CA into the
system trust store. On macOS you will be prompted by Keychain Access. On Linux run
sudo mkcert -install. On Windows run the terminal as Administrator.
Certificate expired
The server auto-regenerates certificates within 30 days of expiry on startup. If the
certificate has already expired, restart the server or run
npx agent-recon tls setup to regenerate immediately.
WSL trust store
If browsers do not trust the certificate on WSL, ensure mkcert -install was
run from WSL (not native Windows). mkcert uses Windows interop to install the CA into the
Windows trust store, which is required for Windows-side browsers to accept the certificate.
Custom cert errors
“ENOENT” or “unable to read” errors when using custom certificates. Verify that the cert and key paths are absolute and that the files are PEM-encoded. Check file permissions to ensure the Node process can read them.
Port conflict on 3132
HTTPS defaults to port 3132. If another process is using that port, change it via the
tls_https_port setting in Settings, or set TLS_PORT=<port>
as an environment variable before starting the server.
Telemetry Heartbeat
The server sends an optional privacy-respecting heartbeat containing only:
{ install_id, version, platform, timestamp }. No PII, session content, or
API keys.
To disable: set telemetry_enabled to 'false' in Settings.
To verify status: GET /api/telemetry/status (not authenticated).