AgentSkillsCN

Rust Tauri Debugging

Rust Tauri 调试

SKILL.md

Rust Tauri Debugging

Debugging techniques for Tauri applications with Rust backend.

When to Use

  • Tauri app crashes or doesn't start
  • IPC commands failing or returning errors
  • State management issues
  • WSL2-specific Tauri problems
  • Build or bundling errors

Quick Diagnosis

App Won't Start

  1. Check console output:

    bash
    # Run with debug output
    RUST_LOG=debug cargo tauri dev
    
  2. Check for GTK/WebKit issues (WSL2):

    bash
    export GDK_BACKEND=x11
    export WEBKIT_DISABLE_COMPOSITING_MODE=1
    cargo tauri dev
    
  3. Check for missing dependencies:

    bash
    # Ubuntu/Debian
    sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
    

IPC Issues

  1. Command not found:

    • Verify function is in generate_handler! macro
    • Check function name matches exactly (no typos)
    • Ensure #[tauri::command] attribute present
  2. Serialization errors:

    • Check types implement Serialize/Deserialize
    • Verify TypeScript types match Rust types
    • Use Result<T, String> for error handling
  3. Arguments not received:

    • Check parameter names match between Rust and TypeScript
    • Ensure complex types are properly serialized

Debugging Techniques

Enable Logging

rust
// src-tauri/src/main.rs
fn main() {
    env_logger::Builder::from_env(
        env_logger::Env::default().default_filter_or("debug")
    ).init();

    tauri::Builder::default()
        // ...
}

// In commands
#[tauri::command]
fn my_command(input: String) -> Result<String, String> {
    log::debug!("my_command called with: {}", input);
    // ...
}

DevTools Console

typescript
// Frontend - check for invoke errors
try {
  const result = await invoke('command_name', { arg: value });
  console.log('Success:', result);
} catch (error) {
  console.error('Invoke failed:', error);
}

Inspect IPC Messages

rust
// Add to tauri.conf.json for dev
{
  "build": {
    "devPath": "http://localhost:5173",
    "beforeDevCommand": "bun dev",
    "withGlobalTauri": true
  }
}

Then in browser console:

javascript
window.__TAURI_IPC__  // Check if available

Common Issues

State Not Persisting

Problem: State resets between command calls

Solution: Use managed state properly:

rust
use std::sync::Mutex;
use tauri::State;

struct AppState {
    data: Mutex<Vec<String>>,
}

#[tauri::command]
fn add_item(item: String, state: State<'_, AppState>) -> Result<(), String> {
    let mut data = state.data.lock().map_err(|e| e.to_string())?;
    data.push(item);
    Ok(())
}

fn main() {
    tauri::Builder::default()
        .manage(AppState {
            data: Mutex::new(Vec::new()),
        })
        .invoke_handler(tauri::generate_handler![add_item])
        .run(tauri::generate_context!())
        .expect("error");
}

Async Commands Blocking

Problem: UI freezes during long operations

Solution: Use async commands:

rust
#[tauri::command]
async fn long_operation() -> Result<String, String> {
    tokio::time::sleep(std::time::Duration::from_secs(2)).await;
    Ok("Done".to_string())
}

Window Events Not Firing

Problem: window.emit() not reaching frontend

Solution: Check event name and ensure listener is registered first:

typescript
// Frontend - set up listener BEFORE triggering backend
import { listen } from '@tauri-apps/api/event';

const unlisten = await listen('my-event', (event) => {
  console.log('Received:', event.payload);
});

// Only now trigger the backend action
await invoke('start_operation');
rust
// Backend
use tauri::Manager;

#[tauri::command]
fn start_operation(app: tauri::AppHandle) {
    // Emit to all windows
    app.emit_all("my-event", "payload").ok();
}

File Dialog Not Working in WSL2

Problem: File dialogs crash or don't appear

Solution: Use Tauri's dialog API with workarounds:

rust
#[tauri::command]
async fn pick_file() -> Result<String, String> {
    let path = tauri::api::dialog::blocking::FileDialogBuilder::new()
        .pick_file()
        .ok_or("No file selected")?;

    Ok(path.to_string_lossy().to_string())
}

If still failing, use native Gtk dialogs or implement custom file picker.

WSL2-Specific Debugging

Check WebKitGTK Installation

bash
pkg-config --modversion webkit2gtk-4.1
# Should return version number

# If missing:
sudo apt install libwebkit2gtk-4.1-dev

Debug GTK Issues

bash
GTK_DEBUG=interactive cargo tauri dev

Check Display Configuration

bash
echo $DISPLAY  # Should be :0 for WSLg
echo $WAYLAND_DISPLAY  # Should be set for WSLg

# Test with simple GTK app
gtk3-demo

Build Debugging

Bundle Fails

bash
# Verbose build
cargo tauri build --verbose

# Check target
cargo tauri build --target x86_64-unknown-linux-gnu

Missing Icons

Ensure icons exist in src-tauri/icons/:

code
icons/
├── 32x32.png
├── 128x128.png
├── 128x128@2x.png
├── icon.icns
├── icon.ico
└── icon.png

AppImage Issues on WSL2

bash
# Install FUSE
sudo apt install fuse libfuse2

# Or extract and run
./my-app.AppImage --appimage-extract
./squashfs-root/AppRun

Useful Commands

bash
# Run with full debug output
RUST_LOG=debug RUST_BACKTRACE=1 cargo tauri dev

# Build for current platform
cargo tauri build

# Generate TypeScript bindings (if using tauri-specta)
cargo test -- --nocapture

# Check Tauri CLI version
cargo tauri --version

# Update Tauri dependencies
cargo update -p tauri -p tauri-build