Diagnostics & Logging
Workbench writes structured, daily-rotated logs to disk so user-reported bugs can be reconstructed from the file alone — without needing the user to reproduce while the app is attached to a debugger. The pipeline is on by default in every build.
Where logs live
Section titled “Where logs live”The default log directory is ~/.workbench/logs/ (sibling to ~/.workbench/workspaces and ~/.workbench/plugins). Files are named workbench.<YYYY-MM-DD>.log (the tracing-appender layout is <prefix>.<date>.<suffix>) and rotate daily. A startup retention sweep deletes anything older than 14 days so the directory never grows unbounded.
You can override the location with the WORKBENCH_LOG_DIR environment variable — handy when running multiple dev instances side-by-side and you want each one’s logs in its own folder.
Every log line carries the process PID, so even when two Workbench instances share the same daily file you can demux by grep pid=NNN.
Settings → Diagnostics
Section titled “Settings → Diagnostics”The Diagnostics section in Settings (between Editor and Git) gives you three controls without ever needing to touch an environment variable.
Log level
Section titled “Log level”Restart-required filter applied to the file log when RUST_LOG is unset. Choose:
| Level | Captures |
|---|---|
default | info overall plus debug for Workbench’s own crates, with mdns_sd capped at warn — the shipping default |
warn | warnings and errors only |
info | startup, shutdown, every Tauri command lifecycle event |
debug | the above plus Workbench debug events and span timings for slow ops (DB migrations, git fetches, diff parsing, agent spawns) |
trace | maximum Workbench detail; not recommended for normal use |
The in-app selector scopes coarse levels to Workbench’s own crates and keeps dependencies below debug/trace. It also caps mdns_sd at warn so mDNS packet chatter does not flood the daily log.
If RUST_LOG is set in the process’s environment, this select is locked and a banner explains why — RUST_LOG always wins so the env var workflow is preserved.
Frontend log verbosity
Section titled “Frontend log verbosity”How much of the React side’s console.* output is mirrored into the daily log. Takes effect immediately, no restart needed.
| Verbosity | Mirrored sources |
|---|---|
| Errors only (default) | uncaught browser errors, promise rejections, React error-boundary catches, console.error |
| Errors + warnings | the above plus console.warn |
| Everything | the above plus console.log and console.info |
Browser-side uncaught errors and unhandled promise rejections are always captured, regardless of verbosity — only the explicit console.* calls are gated. React’s StrictMode warnings are forwarded under “Errors + warnings” if you need to see them in the file log.
Open Logs Folder / Copy path
Section titled “Open Logs Folder / Copy path”Two buttons. The first reveals the log directory in your file manager (Finder on macOS, the system handler on Linux/Windows). The second copies the path to your clipboard — useful for pasting into a bug report.
Filtering verbosity by domain
Section titled “Filtering verbosity by domain”When investigating a specific subsystem, use a per-domain RUST_LOG filter rather than turning every domain up to trace. RUST_LOG is honored verbatim, so it can still enable dependency targets when you deliberately need them. Every event in Workbench is emitted under a workbench::<domain> target:
# Only chat lifecycle eventsRUST_LOG=workbench::chat=trace cargo tauri dev
# Chat plus MCP supervisionRUST_LOG=workbench::chat=trace,workbench::mcp=debug cargo tauri dev
# Everything from the React webviewRUST_LOG=workbench::frontend=trace cargo tauri devEstablished domains:
| Target | Covers |
|---|---|
workbench::startup | process boot, paths, multi-instance warning |
workbench::panic | Rust panic hook output (with backtrace) |
workbench::db | database open, migrations, and compaction |
workbench::chat | turn lifecycle, persistent session reuse / respawn |
workbench::agent | Claude CLI subprocess events |
workbench::backend | alternative-provider gateway |
workbench::mcp | MCP supervisor and server registration |
workbench::plugin | Lua plugin runtime + Claude-Code marketplace |
workbench::git | git CLI shellouts |
workbench::diff | diff parsing |
workbench::scm | PR / CI provider plugin invocations |
workbench::pty | terminal spawn / exit |
workbench::voice | Whisper / Speech.framework |
workbench::ws | WebSocket server (remote workspaces) |
workbench::ipc | local CLI ↔ GUI socket |
workbench::remote | remote-control commands |
workbench::ui | theme load, settings persistence |
workbench::frontend | events forwarded from the React webview |
workbench::updater | update checks, boot probation, and rollback |
Voice click→prompt latency
Section titled “Voice click→prompt latency”Every mic click logs a structured event on the workbench::voice target so the time between clicking the mic and the recorder opening is visible in the daily log. The event fires whether the start succeeded or not:
- Success — info level,
provider_id,total_ms(full command duration), andstream_open_ms(just the cpal input-stream open). - Failure — error level, with the propagated error string (permission denied, model missing, recorder open failure, already-active recording, etc.) on the same target.
2026-05-08T17:32:14.123Z INFO workbench::voice voice_start_recording{provider_id=apple-speech total_ms=312 stream_open_ms=308}: voice start recording latencyTo watch in real time, tail the daily log file (path under Settings → Diagnostics → Open log directory) or run with RUST_LOG=workbench::voice=info cargo tauri dev. Useful when filing a bug report about voice startup feeling slow — copy the matching line into the report and the relevant timings are right there.
There is no cold/warm dimension on the event because Workbench intentionally does not prewarm voice subsystems at launch (this keeps macOS from showing an unprompted microphone permission dialog on boot). Treat every click as a cold start.
File format
Section titled “File format”Logs default to a compact text format (one event per line, with a tab-separated key=value tail of structured fields):
2026-05-08T17:32:14.123Z INFO workbench::chat send_chat_message{chat_session_id=4f… workspace_id=ws-abc…}: turn dispatchedFor machine-parseable output set WORKBENCH_LOG_FORMAT=json before launching — every event becomes a JSON line, which is friendlier to jq post-mortems:
WORKBENCH_LOG_FORMAT=json cargo tauri dev# Later:jq 'select(.target=="workbench::chat" and .level=="ERROR")' \ ~/.workbench/logs/workbench.2026-05-08.logMemory diagnostics
Section titled “Memory diagnostics”Workbench logs detailed memory statistics to help diagnose memory growth in long-running sessions. On Linux with glibc, the logger calls mallinfo2() to report heap metrics — arena size, in-use bytes, free chunks, and mmap’d regions — alongside Workbench’s own per-collection tracking (workspace snapshots, session caches, terminal buffers). On macOS and Windows, the glibc introspection is unavailable but per-collection sizes are still reported.
Memory diagnostics are emitted on the workbench::memory target at debug level. To surface them, set the log level to debug in Settings > Diagnostics (or via RUST_LOG=workbench::memory=debug). The events fire periodically and on collection-size changes, so you can correlate memory growth with specific operations in the timeline.
Webview watchdog
Section titled “Webview watchdog”The Rust backend monitors the frontend webview for unresponsiveness. If the webview stops sending heartbeats while the backend process is still alive, Workbench begins a progressive recovery sequence:
- Soft reload — the backend reloads the webview content, equivalent to a browser refresh. Most transient rendering hangs recover here.
- Window recreation — if the reload doesn’t restore responsiveness, Workbench destroys and recreates the webview window. State is preserved in the Rust backend and rehydrated when the new webview mounts.
- Native tray notification — if recovery still fails, a native notification appears via the system tray so you know the app needs attention, even if the webview is completely unresponsive.
The watchdog runs automatically in the background. No configuration is needed — it activates only when the frontend genuinely stops responding, not during normal heavy renders.
Input-stall watchdog (Linux / Wayland)
Section titled “Input-stall watchdog (Linux / Wayland)”A separate watchdog monitors input dispatch latency on Linux. When a stall is detected (the webview stops processing pointer or keyboard events while the compositor is still delivering them), the watchdog logs the event with correlates (RAF delta, viewport state, render staleness, focus recency, child RSS) and surfaces a passive dismissible toast.
Active recovery (minimizing and refocusing the window) is disabled by default on Linux — every prior attempt made things worse under Wayland compositors without measurably shortening the stall. If you want to experiment with active recovery, enable it with the webview.input_stall_active_recovery app setting or the WORKBENCH_INPUT_STALL_ACTIVE_RECOVERY=1 environment variable.
Dev builds emit a watchdog://debug/input_stall event for the /workbench-debug skill to sample without log tailing.
Hard Reset Webview
Section titled “Hard Reset Webview”If the webview enters a bad state that the automatic watchdog doesn’t catch, you can force a complete webview recreation manually from Help > Hard Reset Webview. This tears down the entire webview and creates a fresh one, reloading all frontend state from the backend. It is the nuclear option — use it when the UI is visually corrupted or frozen but the tray icon and backend are still responsive.
Database compaction on upgrade
Section titled “Database compaction on upgrade”Workbench stores app state in workbench.db under the OS data directory (~/Library/Application Support/ on macOS, $XDG_DATA_HOME/ on Linux, and %APPDATA%/ on Windows unless WORKBENCH_DATA_DIR overrides it).
Older builds left SQLite auto-vacuum disabled, so deleting checkpoint snapshots could free pages inside SQLite without shrinking the database file on disk. The first launch of a build with database compaction may run a one-time SQLite VACUUM to apply incremental auto-vacuum and reclaim those stranded pages. For very large databases, startup can pause while that rewrite finishes. Other database connections wait up to 30 seconds for the maintenance lock before retrying on a later open.
After conversion, routine database opens only check that the compaction mode is in place. High-churn delete paths, such as checkpoint and workspace cleanup, run bounded incremental vacuum work after the delete commits. To inspect this lifecycle, filter logs for the workbench::db target.
Multiple dev instances
Section titled “Multiple dev instances”Workbench has no single-instance lock — running ./scripts/dev.sh twice gives you two independent processes against the same SQLite database. Logs from both instances interleave into the same daily file by default, demuxable via the pid=NNN field on every line.
If you’d rather isolate them, set WORKBENCH_LOG_DIR to a per-instance path before launching. The dev launcher leaves this up to you so you can choose interleaved-in-one-file or separate-files based on what you’re investigating.
On startup, Workbench also scans ${TMPDIR}/workbench-dev/*.json (the discovery files scripts/dev.sh writes) and emits a WARN if another live PID is already running against the same DB path. The warning fires once at boot and explains exactly why it can corrupt persisted state — useful when an old instance lingers and you didn’t notice.
Update boot-health rollback
Section titled “Update boot-health rollback”After an in-app update, Workbench starts the new build on a short probation window (20 s by default). During that window Workbench records boot-stage breadcrumbs in the probation sentinel: process start, webview creation, React mount, initial workspace-data loading, and any initial-data load error. The React app sends the final boot heartbeat after the first commit past the loader (viewStateHydrated is true and loadInitialData resolved). If that heartbeat never arrives, Workbench treats the update as failed to start and tries to restore the previous self-contained install.
When rollback succeeds, the next launch shows a native dialog saying which version failed, which version was restored, and the last boot stage reached. Startup failures, initial workspace-load failures, and slow starts get different wording so the report starts from the right symptom. When rollback cannot be applied (for example, there was no usable previous-install backup or the install location could not be replaced), Workbench writes a no-loop failure report and shows a dialog with the failed version, the captured boot stage, and the release URL to download manually.
A force-quit during the probation window leaves the sentinel on disk; the next launch increments the attempt counter and treats attempts >= 2 as “this build runs” (the user already booted past the timer once), clears the sentinel, and skips arming the timer. This avoids spurious rollbacks on an otherwise healthy build whose user happens to quit during every probation window.
Backups under ~/.workbench/updates/previous/ are pruned to a single generation each time prepare_for_update runs, so the directory does not grow unbounded across updates.
Useful files for a rollback bug report:
| File | Meaning |
|---|---|
~/.workbench/updates/previous/<version>/ | Backup of the previous self-contained install, when one was available. |
OS data dir boot-probation.json | Internal sentinel for the update currently on probation. Cleared after a healthy boot. |
OS data dir boot-rollback-report.json | One-shot report consumed on next launch to show the native rollback dialog. Includes the failed boot stage, probation timeout, rollback execution error (if any), and a bounded tail of the failed build’s daily log. |
~/.workbench/logs/workbench.<DATE>.log | Structured timeline under the workbench::updater target. |
The OS data dir is ~/Library/Application Support/ on macOS, $XDG_DATA_HOME/ on Linux, and %APPDATA%/ on Windows unless WORKBENCH_DATA_DIR overrides it.
Tuning the probation window
Section titled “Tuning the probation window”WORKBENCH_BOOT_PROBATION_SECS overrides the default 20 s probation. Useful for slow first-launch scenarios (e.g. cold-cache Linux builds where libwebkit2gtk dynamic loading dominates startup time) and for shrinking the window while testing the rollback path locally. Values are clamped to the range 1–120 s; non-numeric values fall back to the default. Setting WORKBENCH_BOOT_PROBATION_SECS=2 plus an artificial pre-render hang in the React tree is the recommended way to exercise the rollback dialog from a dev build.
Sandboxed (fresh-user) sessions
Section titled “Sandboxed (fresh-user) sessions”For testing first-run UX (the welcome card, onboarding, plugin seeding) you can launch Workbench with a completely empty data tree:
./scripts/dev.sh --new.\scripts\dev.ps1 --newThe flag points three env vars at a per-PID directory under ${TMPDIR}/workbench-dev/new-<pid>/ ($env:TEMP\workbench-dev\new-<pid>\ on Windows) and removes the tree on exit:
WORKBENCH_HOMEoverrides the~/.workbench/tree (workspaces, plugins, themes, logs, models, packs).WORKBENCH_DATA_DIRoverrides the OS data directory holdingworkbench.db.CLAUDE_CONFIG_DIRoverrides the Claude CLI’s~/.claude/tree, which Workbench reads and writes forsettings.json,.credentials.json, installed plugins, and marketplace registrations. Without this override, a--newrun that touches plugins or auth would write into the real~/.claude/and survive the sandbox tear-down — defeating the “simulate a new user” purpose of the flag.
Cloned (real-state) sessions
Section titled “Cloned (real-state) sessions”--clone is the inverse of --new: it pre-populates a stable sandbox dir (${TMPDIR}/workbench-dev/clone/) with an rsync -a --delete mirror of your real ~/.workbench/, workbench.db, and ~/.claude/, so the dev build inherits your existing workspaces, themes, plugins, and Claude credentials but writes to the sandbox copy rather than the originals. The sandbox is not a copy-on-write snapshot — rsync makes full copies (sized by source) and re-runs sync incrementally, only transferring changed files. The cloned workbench.db is rsync’d raw, so quit the release app before launching if you need a guaranteed-consistent DB snapshot. Mutually exclusive with --new. Currently macOS/Linux only — the PowerShell launcher doesn’t implement --clone.
Nuking stale sandboxes
Section titled “Nuking stale sandboxes”If a --new or --clone session is killed with SIGKILL it misses its cleanup trap and leaves a directory under ${TMPDIR}/workbench-dev/ behind. Run:
./scripts/dev.sh --clean.\scripts\dev.ps1 --cleanto wipe everything under ${TMPDIR}/workbench-dev/ ($env:TEMP\workbench-dev\ on Windows). This is a blunt nuke — no PID check, every new-<pid>/, clone-<pid>/, and <pid>.json is removed regardless of whether the owning process is still alive. If you have a dev session currently running, its sandbox is removed too and the dev app will start seeing missing files mid-session, so prefer to quit running dev instances first. The command exits after the sweep without launching the app.
You can also set any of these env vars manually for a longer-lived sandbox:
WORKBENCH_HOME=/tmp/workbench-demo \WORKBENCH_DATA_DIR=/tmp/workbench-demo/data \CLAUDE_CONFIG_DIR=/tmp/workbench-demo/claude \ ./scripts/dev.sh$env:WORKBENCH_HOME = "$env:TEMP\workbench-demo"$env:WORKBENCH_DATA_DIR = "$env:TEMP\workbench-demo\data"$env:CLAUDE_CONFIG_DIR = "$env:TEMP\workbench-demo\claude".\scripts\dev.ps1Filing a bug report
Section titled “Filing a bug report”- Reproduce the issue.
- Settings → Diagnostics → Copy path to grab the log directory.
- Open the most recent
workbench.<DATE>.log— that’s your timeline. - If you want fuller detail, switch the Log level to
debug, restart Workbench, reproduce, and the file log will include span timings + every command’s entry/exit fields. - Attach the relevant log file to the GitHub issue along with the reproduction steps.
The frontend-bridge default catches React render crashes and uncaught browser errors automatically, so the file log usually has enough context to triage even if you can’t reproduce on demand.
Original source: utensils.io/claudette