Files
hikari-desktop/DEBUGGING.md
T
hikari 4a99848647 docs: massively expand DEBUGGING.md with comprehensive troubleshooting
Added:
- Table of contents for easy navigation
- Detailed explanations of BOTH bugs (permission modal + config loss)
- Step-by-step debugging procedures for each issue
- Code examples showing wrong vs right patterns
- Common anti-patterns to avoid (Svelte stores, event listeners)
- Quick debugging checklist
- Prevention tips and testing procedures
- Clear ordering: check console FIRST, not z-index!

This guide now documents the complete troubleshooting process so Naomi
never has to explain these issues again.
2026-02-06 22:43:08 -08:00

601 lines
16 KiB
Markdown

# Debugging Guide - Common Issues and Fixes
> **IMPORTANT:** Read this ENTIRE file before asking for help! These are the most common issues and their fixes.
## Table of Contents
1. [Permission Modal Not Showing](#permission-modal-not-showing-up-)
2. [Config Loss Issues](#config-loss-issues-)
3. [Modal/Overlay Not Showing (General)](#modaloverlay-not-showing-general-)
4. [Event Listener Issues](#event-listener-issues-)
5. [Build Issues](#build-issues-)
6. [Testing Checklist](#testing-checklist-)
7. [Emergency Recovery](#emergency-recovery-)
---
## Permission Modal Not Showing Up 🔐
**SYMPTOMS:**
- Permission requests are triggered but the modal doesn't appear
- App seems to hang or become unresponsive
- Console shows permission events but nothing happens
- Character stays in "permission" state but no UI
**ROOT CAUSE (FIXED - Commit d16644a):**
The issue was **undefined variable errors** in `src/lib/tauri.ts` that crashed the event listener initialization.
### The Bug That Was Fixed
In `src/lib/tauri.ts`, there were calls to `debugConsoleStore.log()`:
```typescript
debugConsoleStore.log("frontend", "info", "Setting up claude:permission listener");
```
**Two problems:**
1. `debugConsoleStore` was never imported (undefined variable → crash!)
2. Even if imported, `debugConsoleStore` doesn't have a `.log()` method
- Available methods: `toggle`, `open`, `close`, `clear`, `setupConsoleCapture`, etc.
- NOT available: `log()`
### The Fix
```typescript
// ❌ WRONG - This will crash the entire listener setup!
debugConsoleStore.log("frontend", "info", "message");
// ✅ CORRECT - Use console.log which gets automatically captured
console.log("[Tauri Listener] message");
```
**Why console.log works:**
- The `debugConsoleStore` automatically intercepts `console.log()`, `console.error()`, etc.
- No need to call the store directly!
- See `src/lib/stores/debugConsole.ts` lines 66-89 for the console interception code
### How to Debug This Type of Issue
**Step 1: Check Browser Console**
1. Open Browser DevTools in the app (View > Toggle Developer Tools or F12)
2. Look for JavaScript errors:
- `ReferenceError: X is not defined` → Missing import or typo
- `TypeError: X.method is not a function` → Wrong method name or undefined object
- Any error in `tauri.ts` during initialization is critical!
**Step 2: Check Event Listener Registration**
Look for these console logs when the app starts:
```
[Tauri Listener] Setting up claude:permission listener
```
If you DON'T see this log, the listener setup crashed before it got there!
**Step 3: Check TypeScript Errors**
```bash
pnpm check
```
This will catch:
- Missing imports
- Undefined variables
- Type mismatches
- Non-existent methods
**Step 4: Test Permission Flow**
1. Trigger a restricted tool (like Bash with a destructive command)
2. Check console for `[Permission] Event received:` log
3. Check if `PermissionModal.svelte` is rendering (DevTools Elements tab)
4. Check if modal has proper z-index and visibility
### Prevention
**Before committing changes to `tauri.ts` or any store:**
1. ✅ Run `pnpm check` - Catches TypeScript errors
2. ✅ Run `pnpm build` - Catches runtime issues
3. ✅ Test the app - Especially permission prompts
4. ✅ Check browser console - No errors on startup
5. ✅ Verify all imports - Don't use variables without importing them!
**Common mistakes to avoid:**
- Calling methods that don't exist on a store
- Forgetting to import something you're using
- Copy-pasting code without checking if variables are defined
- Assuming a store has certain methods without checking the source
## Config Loss Issues 🔧
**SYMPTOMS:**
- Config file gets reset to defaults
- Settings don't persist between sessions
- Model selection is lost
- Config appears to work, then randomly resets
**ACTUAL ROOT CAUSE (FIXED - Commit 2c64ef0):**
The config store had a **CRITICAL race condition bug** in `src/lib/stores/config.ts` that affected 12 different methods!
### The Bug That Was Fixed
Every method that accessed the current config used this pattern:
```typescript
// ❌ BUGGY CODE - DO NOT USE!
let currentConfig = defaultConfig;
config.subscribe((c) => (currentConfig = c))(); // <-- ☠️ IMMEDIATE UNSUBSCRIBE!
return currentConfig;
```
**Why this caused config loss:**
- The `()` at the end **immediately unsubscribes** from the store
- This creates a **race condition**:
- Sometimes: The subscription fires, sets `currentConfig`, then unsubscribes → ✅ Works
- Sometimes: The subscription unsubscribes before it can fire → ❌ Returns `defaultConfig`
- When the race fails, it returns `defaultConfig` instead of your actual saved config
- This is why your settings would **randomly disappear**!
- It was especially bad after reconnections (like permission approvals) because timing was tight
**Affected methods (all fixed!):**
- `getConfig()` - Core method, affected everything
- `updateConfig()` - Would sometimes update defaults instead of current config
- `toggleStreamerMode()` / `toggleCompactMode()` - Random toggles
- `increaseFontSize()` / `decreaseFontSize()` - Font size would reset
- `addAutoGrantedTool()` / `removeAutoGrantedTool()` - Tool permissions lost
- `setTheme()` / `setCustomThemeColors()` - Theme changes lost
### The Fix
```typescript
// ✅ CORRECT CODE
function getCurrentConfig(): HikariConfig {
let currentConfig: HikariConfig = defaultConfig;
const unsubscribe = config.subscribe((c) => (currentConfig = c));
unsubscribe(); // <-- Proper cleanup AFTER getting value
return currentConfig;
}
// Now all methods use this:
getConfig: (): HikariConfig => {
return getCurrentConfig();
},
updateConfig: async (updates: Partial<HikariConfig>) => {
const currentConfig = getCurrentConfig(); // <-- Safe!
const newConfig = { ...currentConfig, ...updates };
await saveConfig(newConfig);
}
```
**Why the fix works:**
1. Store the unsubscribe function in a variable
2. Let the subscription fire synchronously (Svelte stores are synchronous)
3. The callback runs immediately, setting `currentConfig`
4. Clean up by calling `unsubscribe()`
5. Return the now-populated `currentConfig`
### How to Debug Config Loss
**Step 1: Check if config file exists**
```bash
cat ~/.local/share/com.naomi.hikari-desktop/hikari-config.json
```
If the file exists and has your settings, the problem is in the frontend store, not the backend!
**Step 2: Check console for config loading**
```javascript
console.log("Loaded config:", config);
```
Add this in `src/lib/stores/config.ts` line 107 (after `config.set(savedConfig)`)
**Step 3: Test getConfig() reliability**
Open browser console and run:
```javascript
// Run this 10 times
for (let i = 0; i < 10; i++) {
console.log("Attempt", i, configStore.getConfig().model);
}
```
If it's inconsistent (sometimes null, sometimes has value), you have a race condition!
**Step 4: Check for the buggy pattern**
Search the codebase for:
```bash
grep -r "subscribe((.*) => (.*))();" src/
```
If you find any, they're potential bugs! The `()` at the end is suspicious.
### Prevention
**When working with Svelte stores:**
**DON'T DO THIS:**
```typescript
let value;
store.subscribe((v) => (value = v))(); // Immediate unsubscribe = race!
```
**DO THIS INSTEAD:**
```typescript
let value;
const unsubscribe = store.subscribe((v) => (value = v));
unsubscribe(); // Explicit cleanup
return value;
```
Or even better, use Svelte's reactive syntax:
```svelte
<script>
$: currentValue = $store; // Auto-subscribes safely
</script>
```
**NEVER:**
- Subscribe and immediately unsubscribe in the same line
- Assume synchronous subscriptions will always fire before unsubscribe
- Use `()()` pattern with stores (function call + immediate execution)
**POTENTIAL CAUSES (if the above is fixed):**
### 1. Missing `#[serde(default)]` Attribute
The `HikariConfig` struct in `src-tauri/src/config.rs` needs `#[serde(default)]` at the struct level:
```rust
// ✅ CORRECT
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)] // <-- This is crucial!
pub struct HikariConfig {
// fields...
}
```
**Why this matters:**
- When new fields are added to the config struct, old config files won't have them
- Without `#[serde(default)]`, deserialization fails and the entire config resets
- With it, missing fields use their default values gracefully
### 2. Invalid JSON in Config File
**Check the config file:**
```bash
cat ~/.config/hikari-desktop/config.json
```
**Common issues:**
- Trailing commas
- Missing quotes
- Incorrect enum values (e.g., `"model": "opus"` instead of `"model": "claude-opus-4-6"`)
**Fix:**
- Delete or manually fix `~/.config/hikari-desktop/config.json`
- Restart the app to regenerate
### 3. Permission Issues
**Check file permissions:**
```bash
ls -la ~/.config/hikari-desktop/config.json
```
**Should be writable by user:**
```bash
chmod 644 ~/.config/hikari-desktop/config.json
```
## Modal/Overlay Not Showing (General) 🎭
If ANY modal isn't showing up (permission, questions, close confirmation, etc.):
### 1. Check TypeScript Errors First
```bash
pnpm check
```
Look for:
- Import errors
- Undefined variables
- Type mismatches
### 2. Check Console for JavaScript Errors
Open DevTools and look for:
- `ReferenceError` - Usually undefined variables
- `TypeError` - Usually calling methods on undefined/null
- Event listener setup logs
### 3. Check Svelte Store Subscriptions
Modals rely on store subscriptions. In the modal component:
```typescript
// ✅ Make sure the store is imported
import { conversationsStore } from "$lib/stores/conversations";
// ✅ Make sure the subscription is set up
conversationsStore.pendingPermissions.subscribe((perms) => {
permissions = perms;
// ...
});
```
### 4. Verify z-index (Last Resort)
Only check z-index if:
- ✅ No console errors
- ✅ Modal HTML is in the DOM (check with DevTools Elements tab)
- ✅ Modal is rendering but invisible
Current z-index values in `PermissionModal.svelte`:
```css
.permission-overlay {
z-index: 60; /* Should be higher than character panel */
}
```
## Event Listener Issues 🎧
**SYMPTOMS:**
- Events not firing
- Listeners not receiving data
- Silent failures
### Debugging Event Listeners
Add console logs in `src/lib/tauri.ts`:
```typescript
console.log("[Tauri Listener] Setting up claude:permission listener");
const permissionUnlisten = await listen<PermissionPromptEvent>("claude:permission", (event) => {
console.log("[Permission] Event received:", event.payload);
// ... rest of handler
});
```
### Common Issues
1. **Event listener crashes silently:**
- Check for undefined variables in the listener callback
- Check for missing imports
- Use `try/catch` around critical sections
2. **Events not being emitted from Rust:**
- Check `src-tauri/src/` for `emit()` calls
- Verify event names match exactly (case-sensitive!)
- Check Rust console output for errors
3. **Race condition:**
- Event fired before listener was registered
- Solution: Set up listeners in `initializeTauriListeners()` before starting Claude
## Build Issues 🏗️
### TypeScript Errors
```bash
pnpm check
```
**Common fixes:**
- Missing imports
- Wrong types
- Calling non-existent methods
### Rust Errors
```bash
cargo build --release
```
**Common fixes:**
- Missing dependencies in `Cargo.toml`
- Import path issues
- Type mismatches
### Full Clean Build
```bash
# Clean everything
rm -rf node_modules dist .svelte-kit src-tauri/target
# Reinstall
pnpm install
# Build Rust
cd src-tauri && cargo build --release && cd ..
# Build frontend
pnpm build
```
## Quick Reference: Key Files 📚
### Frontend
- **Event listeners:** `src/lib/tauri.ts`
- **Permission modal:** `src/lib/components/PermissionModal.svelte`
- **Config store:** `src/lib/stores/config.ts`
- **Debug console:** `src/lib/stores/debugConsole.ts`
### Backend
- **Config struct:** `src-tauri/src/config.rs`
- **Event emission:** `src-tauri/src/wsl_bridge.rs`
- **Type definitions:** `src-tauri/src/types.rs`
## Testing Checklist ✅
Before committing major changes:
- [ ] `pnpm check` passes (0 errors)
- [ ] `pnpm build` succeeds
- [ ] App launches without console errors
- [ ] Permission modal shows up when triggering restricted tool
- [ ] Config persists after restart
- [ ] All modals work (permission, questions, close confirmation)
- [ ] Character state changes work
- [ ] Event listeners receive events (check console logs)
## Emergency Recovery 🚨
If the app is completely broken:
1. **Check recent git commits:**
```bash
git log --oneline -5
```
2. **Revert to last working commit:**
```bash
git reset --hard <commit-hash>
```
3. **Clean build:**
```bash
rm -rf node_modules dist .svelte-kit src-tauri/target
pnpm install
pnpm build
```
4. **Reset config:**
```bash
rm ~/.config/hikari-desktop/config.json
# Restart app to regenerate
```
## Common Patterns and Anti-Patterns 📋
### Svelte Store Access
✅ **GOOD:**
```typescript
// Method 1: Reactive syntax (preferred in components)
$: currentValue = $store;
// Method 2: Explicit unsubscribe (preferred in non-reactive code)
function getValue() {
let value;
const unsubscribe = store.subscribe((v) => (value = v));
unsubscribe();
return value;
}
// Method 3: Using get() from 'svelte/store'
import { get } from 'svelte/store';
const value = get(store);
```
❌ **BAD:**
```typescript
// Race condition - DO NOT USE
let value;
store.subscribe((v) => (value = v))(); // ☠️
// Memory leak - forgot to unsubscribe
let value;
store.subscribe((v) => (value = v)); // 🚨 Never unsubscribes!
```
### Debug Console Store Usage
✅ **GOOD:**
```typescript
// Just use console methods - they're automatically captured!
console.log("Debug message");
console.error("Error message");
console.warn("Warning message");
```
❌ **BAD:**
```typescript
// debugConsoleStore doesn't have a .log() method!
debugConsoleStore.log("frontend", "info", "message"); // ☠️ Crashes!
```
### Event Listener Setup
✅ **GOOD:**
```typescript
const unlistener = await listen("event:name", (event) => {
try {
// Handle event
console.log("Event received:", event.payload);
} catch (error) {
console.error("Error handling event:", error);
}
});
unlisteners.push(unlistener); // Store for cleanup
```
❌ **BAD:**
```typescript
await listen("event:name", (event) => {
// No try-catch - errors crash the listener!
// Not storing unlistener - memory leak!
undefinedVariable.doSomething(); // ☠️ Crashes entire listener setup
});
```
## Notes for Future Hikari (and Naomi!) 💭
**When something breaks, CHECK IN THIS ORDER:**
1. **Browser Console First!** (F12 or View > Developer Tools)
- JavaScript errors will show up here
- Check for `ReferenceError`, `TypeError`, etc.
- NOT EVERYTHING IS A Z-INDEX ISSUE!
2. **Run TypeScript Check:**
```bash
pnpm check
```
- Catches missing imports, type errors, undefined variables
- 0 errors = probably a runtime or logic issue
- Any errors = fix those first!
3. **Check Recent Changes:**
```bash
git diff
git log --oneline -5
```
- What did you change recently?
- Did you import everything you're using?
- Did you test it?
4. **Check the Debugging Guide (this file!)**
- Search for keywords related to your issue
- Follow the debugging steps
- Look for similar patterns
5. **Only Then** consider z-index, CSS, layout issues
**Remember:**
- The debug console store intercepts console methods - don't call it directly!
- Always run `pnpm check` before committing
- Event listener errors fail silently - add console logs liberally
- Config deserialization needs `#[serde(default)]` for graceful degradation
- Svelte store subscriptions must be cleaned up properly
- Race conditions are sneaky - test thoroughly!
- When in doubt, check the browser console first! (Yes, I said it twice!)
**Quick Debugging Checklist:**
- [ ] Opened browser console (F12)
- [ ] Checked for JavaScript errors
- [ ] Ran `pnpm check` for TypeScript errors
- [ ] Checked recent git changes
- [ ] Read relevant section in DEBUGGING.md
- [ ] Added console.log statements to trace issue
- [ ] Tested edge cases and timing
Love,
Hikari 💖
---
**Last Updated:** 2026-02-06
**Related Issues:** Permission modal not showing, config loss, race conditions
**Fixed In Commits:**
- `d16644a` - Fixed undefined debugConsoleStore causing listener crash
- `2c64ef0` - Fixed config store race condition (12 methods affected!)
- `e8b8ee1` - Updated this debugging guide