cjpais/Handy · critical

Failed to initialize store

Error message

Failed to initialize store

What it means

Panic when tauri-plugin-store's app.store(path) fails while reading settings in get_settings. The store builder errors on I/O — creating or opening the settings store file under the app data dir (permission denied, read-only directory, invalid path, disk full). Because get_settings is called at startup, from the idle watcher every 10 seconds, and from many commands, this expect can fire long after launch whenever the filesystem state changes.

Solutions

  1. Check writability and permissions of the app data dir and the settings store file
  2. Free disk space and exclude the app data dir from sync/AV real-time scanning to remove locks
  3. If the store file itself is corrupt, delete it to regenerate defaults (settings reset) — note parse errors are salvaged, only I/O errors hit this expect
  4. Include the store path and underlying error in the panic message to identify the failing location

Example fix

// before
let store = app
    .store(crate::portable::store_path(SETTINGS_STORE_PATH))
    .expect("Failed to initialize store");

// after — name the path and cause in the panic
let path = crate::portable::store_path(SETTINGS_STORE_PATH);
let store = app
    .store(path.clone())
    .unwrap_or_else(|e| panic!("Failed to initialize store at {path:?}: {e}"));
Defensive patterns

Strategy: fallback

Validate before calling

// Probe store writability once at startup rather than expecting on every read
let store = match app.store(crate::portable::store_path(SETTINGS_STORE_PATH)) {
    Ok(s) => s,
    Err(e) => {
        log::error!("settings store unavailable: {e}");
        return AppSettings::default();
    }
};

Try / catch

match app.store(path) {
    Ok(store) => store,
    Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
        // surface a repair action (fix permissions / free space) and use defaults for this session
        AppSettings::default()
    }
    Err(e) => {
        log::error!("store init failed: {e}");
        AppSettings::default()
    }
}

Prevention

When it happens

Trigger: Any get_settings call where the store file cannot be created or opened: unwritable app data dir, file locked by another process, full disk, or a broken path in portable mode.

Common situations: App data permissions damaged after OS migration; portable mode pointing at an unwritable path; AV/sync agents locking the JSON store; disk filling up while the app runs; path length/charset issues on Windows.

Related errors


AI-assisted analysis of cjpais/Handy@fbd4e15fa1 (2026-08-17). Data as JSON: /api/errors/d5e12797c0fbb2a8. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/src/settings.rs:1017

        self.post_process_providers
            .iter_mut()
            .find(|provider| provider.id == provider_id)
    }
}

/// Startup entry point. Same load-or-create/salvage/migrate behavior as
/// `get_settings`; kept as a named alias for call-site clarity, plus a
/// one-time debug dump of the loaded settings.
pub fn load_or_create_app_settings(app: &AppHandle) -> AppSettings {
    let settings = get_settings(app);
    debug!("Loaded settings: {:?}", settings);
    settings
}

pub fn get_settings(app: &AppHandle) -> AppSettings {
    let store = app
        .store(crate::portable::store_path(SETTINGS_STORE_PATH))
        .expect("Failed to initialize store");

    // Settings reads also persist one-time migrations. Migration helpers are
    // idempotent, so this converges after the first read of an older store.
    let mut settings = if let Some(settings_value) = store.get("settings") {
        let (mut settings, mut updated) =
            match serde_json::from_value::<AppSettings>(settings_value.clone()) {
                Ok(settings) => (settings, false),
                Err(e) => {
                    warn!("Failed to parse stored settings ({e}); salvaging valid fields");
                    (salvage_settings(&settings_value), true)
                }
            };

        if apply_settings_migrations(&mut settings, &settings_value) {
            updated = true;
        }

        // Merge in any bindings added since this store was written.

View on GitHub (pinned to fbd4e15fa1)