tauri-debugger
Debug Tauri 2.x desktop app issues including dialogs, CSP, plugins, and Vite integration. Triggers on Tauri dialog not opening, file save dialog doesn't work, CSP errors, virtual:pwa-register errors,
SKILL.md.claude/skills/tauri-debugger/SKILL.md
GitHubARCHIVED: Tauri has been replaced by Electron. This skill is no longer active. For Electron desktop app work, use the
electronskill instead. Historical reference kept below for context.
Tauri Debugger Skill
Debug Tauri 2.x desktop app issues including dialogs, CSP, plugins, and Vite integration.
Trigger Patterns
- Tauri dialog not opening / not appearing
- File save dialog doesn't work
- CSP (Content Security Policy) errors in Tauri
virtual:pwa-registererrors- Click handlers not working in Tauri
- Tauri dev vs build differences
- XDG Portal issues on Linux
Quick Diagnostics
1. Check Tauri Detection in Vite
// vite.config.ts should have:
const isTauri =
process.env.TAURI_ENV_PLATFORM !== undefined || // Build
process.env.TAURI_DEV !== undefined // Dev
// tauri.conf.json must set TAURI_DEV in dev:
"beforeDevCommand": "TAURI_DEV=true npm run dev"
2. Check XDG Portal (Linux)
# Portal service running?
systemctl --user status xdg-desktop-portal
# zenity installed (fallback)?
which zenity || echo "INSTALL: sudo apt install zenity"
# FileChooser portal working?
gdbus call --session \
--dest org.freedesktop.portal.Desktop \
--object-path /org/freedesktop/portal/desktop \
--method org.freedesktop.DBus.Properties.Get \
org.freedesktop.portal.FileChooser version
3. Check PWA Plugin Configuration
WRONG (causes CSP errors):
!isTauri && VitePWA({ ... }) // No stub modules provided!
CORRECT:
VitePWA({
disable: isTauri, // Provides empty stub modules
...
})
4. Check Dialog Plugin (Cargo.toml)
# For Linux with XDG Portal support:
tauri-plugin-dialog = { version = "2.6", default-features = false, features = ["xdg-portal"] }
# NOTE: Can't enable both gtk3 AND xdg-portal - they conflict!
5. Check Capabilities (src-tauri/capabilities/default.json)
{
"permissions": [
"dialog:default",
"dialog:allow-save",
"dialog:allow-open",
"fs:default",
{
"identifier": "fs:allow-write-text-file",
"allow": [
{ "path": "$DOWNLOAD/**" },
{ "path": "$HOME/**" }
]
}
]
}
Common Issues & Solutions
Issue: CSP Error virtual:pwa-register/vue
Symptoms:
- Console shows "Refused to load virtual:pwa-register/vue because it does not appear in the script-src directive"
- Click handlers don't fire
- Vue events seem broken
Root Cause: PWA plugin conditionally excluded but import statements still exist in code. No stub modules provided.
Solution:
// vite.config.ts
VitePWA({
disable: isTauri, // This provides empty stub modules
...
})
Issue: isTauri is false during tauri dev
Symptoms:
- PWA enabled in Tauri dev mode
- Tauri-specific code not running
Root Cause: TAURI_ENV_PLATFORM only set during tauri build, not tauri dev.
Solution:
// tauri.conf.json
{
"build": {
"beforeDevCommand": "TAURI_DEV=true npm run dev"
}
}
Issue: Dialog doesn't appear on Linux
Symptoms:
dialog.save()returns immediately with no dialog- No errors in console
Checklist:
- Install zenity:
sudo apt install zenity - Check XDG Portal is running:
systemctl --user status xdg-desktop-portal - Use xdg-portal feature in Cargo.toml (not gtk3)
- Ensure DISPLAY/WAYLAND_DISPLAY env vars are set
Issue: Blocking dialog freezes app
Symptoms:
- App hangs when calling
blocking_save_file()
Root Cause: Blocking API can't acquire GTK MainContext from certain threads.
Solution: Use async API instead:
#[tauri::command(async)]
async fn save_file(app: AppHandle) -> Option<PathBuf> {
app.dialog()
.file()
.save_file()
.await
.map(|p| p.into_path())
.flatten()
}
Key Files Reference
| File | Purpose |
|---|---|
vite.config.ts |
Tauri detection, PWA disable |
src-tauri/tauri.conf.json |
beforeDevCommand, CSP, devUrl |
src-tauri/Cargo.toml |
Plugin versions and features |
src-tauri/capabilities/default.json |
Permissions for dialogs, fs |
src-tauri/src/lib.rs |
Rust commands |
Version Compatibility
| Package | Minimum Version | Notes |
|---|---|---|
@tauri-apps/plugin-dialog |
2.6.0 | XDG Portal support |
@tauri-apps/plugin-fs |
2.4.5 | Better error messages |
tauri-plugin-dialog (Rust) |
2.6 | xdg-portal feature |
zenity (Linux) |
any | Fallback dialog renderer |
WebKitGTK Testing Workflow (CRITICAL — read before debugging)
The Problem with Blind Deploys
WebKitGTK bugs only appear in Tauri — NOT in Chrome/Firefox/Safari. Deploying fixes without testing in WebKitGTK leads to wasted build cycles. Use these testing methods:
Method 1: Python WebKitGTK 4.1 Wrapper (Fastest for CSS)
Uses the exact same engine as Tauri's wry (libwebkit2gtk-4.1). Injects .tauri-app class for CSS parity.
# Start Vite on port 6366
npx vite --port 6366 &
# Open WebKitGTK 4.1 window with Tauri CSS parity
python3 scripts/webkit-dev.py
- CSS changes auto-reload via HMR — no Rust rebuild
- Right-click → Inspect Element for DevTools
- Limitation: Cannot reproduce CSP issues or
tauri://protocol bugs (useshttp://localhost)
Method 2: cargo tauri dev (For Tauri-specific bugs)
# Terminal 1: Start Vite manually
TAURI_DEV=true npm run dev
# Terminal 2: Run Tauri (skip beforeDevCommand)
cargo tauri dev --no-dev-server-wait
Method 3: Epiphany (GNOME Web) — WRONG ENGINE
Epiphany uses WebKitGTK 6.0 (GTK4), Tauri uses WebKitGTK 4.1 (GTK3). Different rendering behavior. DO NOT rely on Epiphany for Tauri bug reproduction.
When Each Method Works
| Issue Type | Python Wrapper | cargo tauri dev | Production Build |
|---|---|---|---|
| CSS layout/clipping | ✅ | ✅ | ✅ |
CSS with .tauri-app overrides |
✅ (injected) | ✅ | ✅ |
| CSP blocking styles | ❌ | ❌ (no CSP in dev) | ✅ only |
tauri:// protocol issues |
❌ | ❌ | ✅ only |
| NPopover/dropdown visibility | ✅ | ✅ | may differ (CSP) |
Key Insight: Dev vs Production Differences
- Dev mode (
cargo tauri dev): Loads fromhttp://localhost:5546— no CSP nonce injection - Production build (
cargo tauri build): Loads fromtauri://localhost/— CSP nonces added to all<style>tags at compile time - Runtime-injected
<style>tags (from CSS-in-JS like Naive UI's css-render) get silently blocked in production
CSP + CSS-in-JS (Naive UI) — CRITICAL
The Problem
Naive UI uses css-render which injects <style> tags at runtime via document.head.appendChild(). In Tauri production builds, the CSP nonce system blocks these runtime-injected styles because they lack the compile-time nonce. This makes components like NPopover, NDropdown, NSelect invisible (DOM exists but has zero styling).
The Fix
// src-tauri/tauri.conf.json → app.security
{
"dangerousDisableAssetCspModification": ["style-src"],
"csp": {
"style-src": "'self' 'unsafe-inline' https://fonts.googleapis.com",
...
}
}
dangerousDisableAssetCspModification: ["style-src"] tells Tauri NOT to add nonces to style-src, so 'unsafe-inline' actually works for css-render's runtime style injection.
Reference: Tauri Discussion #8578
Diagnosing CSP Issues
Build with devtools enabled to see CSP violation errors:
cargo tauri build --debug
# Then right-click → Inspect → Console
# Look for: "Refused to apply inline style because it violates..."
NPopover Tips for Tauri
rawprop is fine — strips Naive UI wrapper, uses custom stylingto="body"teleport works correctly with CSP fix- If dropdown appears with double shadow: check if
rawis missing (Naive UI wrapper adds its own background/shadow)
WebKitGTK CSS Gotchas (confirmed bugs)
| Issue | Cause | Fix |
|---|---|---|
| Sidebar items 24px wide | contain: layout on parent |
Remove layout from contain |
overflow: clip hides content |
Not supported in WebKitGTK | Use overflow: hidden + /* WebKitGTK-safe */ |
display: inline-flex collapses in flex chains |
WebKitGTK sizes to intrinsic content | Use display: flex; width: 100% |
| Fonts show as "serif" | False positive in test — "sans-serif" contains "serif" | Use regex /(?<![a-z-])serif/ |
Nested <button> breaks clicks |
Invalid HTML per spec | Use <span role="button" tabindex="0"> |
Related Skills
supabase-debugger- For Supabase connection issues in Tauridev-debugging- General Vue/Pinia debuggingtauri- Build, sign, deploy pipeline