Documentation
Desktop troubleshooting
Identify the failing process, inspect the real endpoint and data directory, and recover without confusing preview with persistence.
Identify the runtime before changing it
A desktop session has separate failure boundaries: Vite supplies frontend assets during development; the Tauri Rust process owns native integration; the Bun sidecar serves application requests and persistence; the SolidJS webview connects to that API. A blank window, a boot timeout, a 401 response, and an empty project list can have different causes.
| Launch mode | Backend | What it can establish |
|---|---|---|
Desktop package dev with no sidecar URL | In-memory fake client | Supported fixture-based screen behavior, not durable application storage. |
| Vite with URL and token | Selected running sidecar | Browser-to-server requests and persistence in that server's directory. |
Root desktop:dev | Compiled supervised sidecar | Native shell, webview, bundled server/resources, and application flow. |
Desktop tauri:dev with no existing binary | Generated stub sidecar | Isolated shell behavior; the stub is not the full application API. |
Record the launch command and whether the endpoint came from Tauri or Vite variables. Do not delete a database or reinstall dependencies merely because a different runtime shows different data.
Understand startup timing
The Rust shell generates a launch token, resolves application data and migration directories, and spawns the target sidecar. The child binds 127.0.0.1 on an ephemeral port. It waits until its public /api/health answers, then writes a JSON ready event with that port. The webview invokes get_sidecar_endpoint and creates its managed API runtime.
| Budget | Current value | Meaning |
|---|---|---|
| Shell ready handshake | 60 seconds per start | Maximum wait for the child's usable ready event. |
| Automatic supervisor restart | One | A failed child is not restarted indefinitely. |
| Native endpoint wait | 125 seconds | Two ready budgets plus slack. |
| Webview connection attempt | 30 seconds | Health/connection timeout for an attempt. |
| Webview retry loop | 150 seconds, 750 ms pauses | Re-resolves endpoints before offering a timed-out failure. |
Retry restarts webview connection attempts; it does not replenish an exhausted Rust supervision budget. Fix the startup cause and relaunch the app when the supervisor has reached its terminal failed state. A port from before a child restart can be stale.
Check ports and health separately
Vite uses localhost:1420 with strict port selection, matching Tauri's configured development URL. It fails instead of silently moving to another port. The sidecar port is unrelated and changes between launches. The companion HTTPS port is a third listener, default 8443, and Studio uses loopback 4396.
CADCAM_URL=http://127.0.0.1:43111
CADCAM_TOKEN=local-development-token
curl --max-time 5 --fail-with-body "$CADCAM_URL/api/health"
curl --max-time 5 --fail-with-body "$CADCAM_URL/health" -H "Authorization: Bearer $CADCAM_TOKEN"
curl --max-time 5 --fail-with-body "$CADCAM_URL/version" -H "Authorization: Bearer $CADCAM_TOKEN" The public health route returns status and timestamp. Authenticated system routes expose readiness and the application version. A healthy process does not prove all providers, database queries, or native dialogs work. Compare the Health API and System API when selecting probes.
If port 1420 is occupied, identify and stop the existing development server deliberately. If a remembered API port refuses connections, read the current ready event or re-resolve the Tauri endpoint rather than guessing neighboring ports.
Reproduce with an isolated sidecar
A source process is useful for separating native-bundle issues from server behavior. Run this from the repository root in an interactive terminal; it creates a fresh data directory and uses the generated migration folder explicitly.
CADCAM_DIAGNOSTIC_DIR=$(mktemp -d /tmp/cadcamfun-desktop-guide.XXXXXX)
CADCAMFUN_TOKEN=local-development-token CADCAMFUN_DATA_DIR="$CADCAM_DIAGNOSTIC_DIR/data" CADCAMFUN_MIGRATIONS_DIR="$PWD/packages/db/drizzle" CADCAMFUN_AI_OFFLINE=true bun --no-env-file apps/sidecar/src/main.ts Copy the announced port into the probe variables in a second terminal. Offline AI mode avoids depending on a configured remote model for basic startup diagnostics; it does not run an alternative local model. The server observes stdin EOF, an explicit shutdown event, and normal runtime interrupts.
A source process uses current files. A native process uses the compiled executable already staged under src-tauri/binaries. If only the compiled run fails, rebuild through bun run build:sidecar and inspect bundling/migration resources. Editing TypeScript does not replace an already running executable.
Resolve the actual database path
| Process/tool | Path selection |
|---|---|
| Tauri-launched sidecar | Tauri app_data_dir(), then cadcamfun.db. |
| Manual sidecar | CADCAMFUN_DATA_DIR/cadcamfun.db. |
| Migration CLI | DATABASE_URL, otherwise ./data/cadcamfun.db relative to its process directory. |
Root db:migrate wrapper | Runs in the database package, normally giving packages/db/data/cadcamfun.db by default. |
| Local Studio API | CADCAMFUN_DB, then DATABASE_URL, then repository-root data/cadcamfun.db. |
These files are not aliases. Match the selected process path before investigating missing drawings. Browser fixtures never open these SQLite files. The sidecar applies migrations only when its configuration supplies a migration folder; omitting it does not automatically discover the source-tree folder.
The database layer enables WAL and foreign keys. Before an existing database upgrade it writes an automatic pre-migration backup and retains five matching backups. It does not create such a backup on every save, unchanged startup, first creation, or in-memory test. Preserve a consistent backup through the database/recovery procedure; copying only the main file of a running WAL database can omit committed WAL content.
Read protocol output and logs
In a source sidecar terminal, stdout is reserved for ready/error protocol events and JSON application logs go to stderr. In the native app, the Rust logging plugin captures shell-side events, including supervised child output. The shell redacts lines that may contain its launch credentials.
The native get_logs_dir command resolves the operating-system application log directory. It is more reliable than assuming one universal path across systems. A developer diagnostic inside the webview can invoke it through the existing Tauri command interface; native path opening is constrained to permitted folders and refuses executable file types.
The reusable boot-screen component supports an optional View logs callback, but the current App.tsx does not pass that callback. Do not assume a View logs button will be visible during a startup failure. Keep the development launching terminal available or use the native log directory through an appropriate diagnostic surface.
For a report, include the launch mode, non-secret data/migration paths, elapsed wait, current API version, exact error tag/message, and last relevant startup events. Redact launch/session/device tokens, pairing links, model keys, and signing material.
Distinguish authentication from empty data
The browser real-client mode requires both VITE_SIDECAR_URL and VITE_SIDECAR_TOKEN. A URL without a token does not activate the fake fallback; endpoint resolution fails. Variables must be supplied before Vite starts. Updating a parent shell does not reconfigure a server already running.
VITE_SIDECAR_URL=http://127.0.0.1:43111 VITE_SIDECAR_TOKEN=local-development-token bun run --filter @cadcamfun/desktop dev Protected calls can use account session context in addition to the application token. Companion and standalone bridge tokens have narrower scopes and are not substitutes for a launch token. A 401 needs credential diagnosis; a 403 needs operation/access diagnosis; an absent or inaccessible project can be reported as 404.
Some screens, including project child-resource tabs, currently turn individual list errors into empty arrays. A blank tab is not sufficient evidence that records were deleted. Read the corresponding project, drawing, or toolpath endpoint before recreating work.
A valid launch token is 64 hexadecimal characters (32 random bytes) and changes on every launch. A token of a different shape was not issued by the desktop shell.
Recognize preview and native limitations
The fake API handles selected projects, drawings, materials, tools, machines, toolpaths, KV, and generator paths. Unsupported collections return HTTP 501 with a preview-specific message. Fixture data belongs to the running page. A successful preview action cannot demonstrate persisted organization, plugin, billing, or live provider behavior.
Native dialogs, keychain operations, tray, updater, and macOS materials require the Tauri host and the correct capabilities. A registered command can still be unavailable to the webview under its capability configuration. For file opening, the shell registers supported associations and forwards paths; that alone does not prove a particular editor imported the contents.
Graphics failures are another boundary. A non-zero container size, visible entities/layers, unlocked selection, and supported WebGL are prerequisites for the relevant viewport interactions. A rendered mesh is evidence of rendering, not a successful save or CAM verification. Use the viewport diagnostics for worker capacity, timeout, and four-view behavior.
Recover preference and display issues
The shared webview preference record is cadcamfun.settings. Defaults include millimetres, system theme, three decimals, GRBL preference, snap enabled, tolerance 0.01 mm, and glass enabled. Stored fields are validated independently; invalid values fall back without making the shell fail. Full/blocked local storage can prevent persistence.
Changing display units does not rescale the stored CAD geometry. Document tolerance is propagated through the shared CAD/generator/verification settings helper. Some feature/rotary values explicitly use degrees; drawing angles use radians. Verify the field contract before diagnosing a numerical mismatch as corrupt geometry.
Glass behavior depends on Tauri/macOS support. The implementation uses Liquid Glass where supported, vibrancy on older macOS through the plugin, and an opaque fallback when unavailable or disabled. Theme/material changes do not alter geometry, project ownership, or machining evidence.
Prefer the relevant settings or feature control over clearing all webview storage. That storage can also contain session/editor handoffs. A visual preference reset is not a database repair procedure.
Separate UI recovery from machining evidence
A loaded project, successful HTTP save, animated toolpath, and generated NC are different observations. Before relying on a saved machining result, inspect the exact NC, source drawing/stock, tool assemblies, machine envelope, selected post, and verification release/issues for that result.
The CAM snapshot code preserves kernel evidence only when the current NC text and its hash match. Portable program writes validate document consistency and may re-evaluate captured kernel requests. Editing NC or changing stock can invalidate previous evidence. Reopening plain NC can parse a program without restoring the same manufacturing proof.
After a crash or interrupted save, first inspect the saved row and its versions. The CAM save modal records the previous program before an update, while toolpath detail manual save records the updated revision. Project history is separate again. Use the history comparison to choose a recovery path.
Do not convert an unresolved check into an accepted operation because the UI recovered. Re-run the appropriate verification against the actual resources and NC; an application health probe has no authority over machine safety.
Triage one failure at a time
- Record launch mode, process versions, ports, and actual data directory.
- Probe health, then one protected read. Separate connectivity from authorization.
- Inspect structured startup/migration errors before resetting or deleting anything.
- Reproduce against an isolated source process when a compiled/native issue is suspected.
- Fix the identified cause, restart the appropriate process, and update stale endpoint variables.
- Reopen the saved document/program and inspect persisted state, versions, exports, and current verification.
Implementation anchors are app/App.tsx, app/sidecar.ts, src-tauri/src/{sidecar,commands,lib}.rs, sidecar main.ts/config.ts/shutdown.ts, and the database lifecycle. Local development supplies configuration details; companion pairing handles its separate network boundary.
Startup checklist
- Is the sidecar binary present for this platform (
src-tauri/binaries/sidecar-<target triple>)?desktop:devbuilds it;tauri:devmay use a stub. - Are migrations staged in
src-tauri/resources/migrations?bun run build:sidecarorbun scripts/build.ts --only=resourcesrestages them. - Did the sidecar print
readywithin 60 seconds? Look for anerrorline on stdout and JSON logs on stderr in the log directory (get_logs_dir). - Did it restart? Only one automatic restart is allowed; a second failure leaves the status at
failed. - Does
GET /api/healthanswer on the reported port? If it does but protected routes return 401, the token belongs to a different launch. - Is the data directory writable and is the database file not held open by another process with incompatible migrations?
| Timer | Value | Owner |
|---|---|---|
| Ready handshake | 60 s per attempt | Rust supervisor |
| Automatic restarts | 1 | Rust supervisor |
| Endpoint wait | 125 s (two handshakes + 5 s) | get_sidecar_endpoint |
| Webview recovery budget | 150 s, 30 s per attempt, 750 ms between attempts | Webview |
| Shutdown grace | 2 s after the shutdown line, then the process is reaped (up to 5 s) | Rust supervisor |
Reading the logs
Sidecar logs are JSON lines on stderr, kept separate from the stdout handshake. Lines that could contain credentials are replaced by [redacted: line may contain credentials] before they are written, so a redacted line in a log is expected, not corruption. If the handshake itself carried such a line, the reported startup error is the redaction marker; read the surrounding stderr lines for context.
The log directory is platform-specific. Get it from the get_logs_dir command rather than guessing a path, and open it with open_path, which accepts the application log directory.
Error messages and what they mean
| Message | Where it comes from | What to do |
|---|---|---|
| The 3D mesh queue is full. Reduce the scene complexity. | Viewport meshing queue (128 jobs) | Hide or simplify bodies; the queue was cleared |
| 3D meshing timed out. Reduce the scene complexity. | One meshing job exceeded 15 s | Simplify the solid; the worker was restarted |
| No AI provider is configured. Add an API key in the AI settings. | Assistant stream, code: "config" | Add a provider key; nothing else is wrong |
| Legacy migration journal does not match local migrations | Database upgrade at startup | Keep the data directory; install the matching version |
| Pull request is not open | Merging or closing a merged/closed PR | Refresh the PR list |
| Unknown revision <rev> | History restore or log with a bad revision | Pick a revision from the log |
| Plugin <id> may not use "<capability>": it was not granted at install time | Plugin context | Reinstall and grant the capability, or change the plugin |
| The machine's rotary axes cannot reach this work plane within their limits | Indexed machining | Use another setup for that face |