libnyanpasu/clash-nyanpasu · error

failed to open storage: {e}

Error message

failed to open storage: {e}

What it means

After extracting hotkey strings from config.yaml, the migration opens the KV storage database via Storage::try_new(storage_path) to write the migrated values. If the storage backend cannot be created or opened (I/O error, corrupt database file, locked file, bad path), this error aborts the migration before any hotkeys are written.

Source

Thrown at backend/tauri/src/core/migration/modules/storage.rs:100

        let Some(hotkeys_value) = config.get(&hotkeys_key).cloned() else {
            return Ok(());
        };
        let Some(hotkeys) = hotkeys_value.as_sequence() else {
            return Ok(());
        };

        let hotkey_strings: Vec<String> = hotkeys
            .iter()
            .filter_map(|value| value.as_str().map(ToString::to_string))
            .collect();

        if !hotkey_strings.is_empty() {
            let storage_path = ctx.storage_path();
            if let Some(parent) = storage_path.parent() {
                std::fs::create_dir_all(parent)?;
            }
            let storage = Storage::try_new(&storage_path)
                .map_err(|e| anyhow::anyhow!("failed to open storage: {e}"))?;

            storage
                .set_item("hotkeys", &hotkey_strings)
                .map_err(|e| anyhow::anyhow!("failed to save hotkeys: {e}"))?;

            // Note: registration is intentionally NOT done here. This migration
            // runs in a separate `migrate` subprocess with no Tauri app handle, so
            // `Hotkey::update` would fail; `Hotkey::init` reads the migrated value
            // from KV storage at app startup instead.
            tracing::info!("migrated {} hotkeys to KV storage", hotkey_strings.len());
        }

        config.remove(&hotkeys_key);
        let new_config = serde_yaml::to_string(&config)
            .map_err(|e| anyhow::anyhow!("failed to serialize config: {e}"))?;
        crate::core::migration::fs::atomic_write(&config_path, new_config.as_bytes())?;

        Ok(())

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Close all running Nyanpasu instances (and the migrate subprocess) so the storage file is not locked, then retry.
  2. Check {e} for the underlying cause: fix directory permissions or free disk space at the storage path.
  3. If the KV database file is corrupt, back it up and remove/rename it so a fresh storage is created (other stored settings may need re-entry).
  4. Exclude the app data directory from antivirus/controlled-folder-access interference and verify the path is writable.
  5. Re-run the migration after remediation; config.yaml was not modified on this failure path.

Example fix

// before
let storage = Storage::try_new(&storage_path)
    .map_err(|e| anyhow::anyhow!("failed to open storage: {e}"))?;
// after
let storage = Storage::try_new(&storage_path)
    .with_context(|| format!("failed to open storage at {} — close running instances and check permissions", storage_path.display()))?;
Defensive patterns

Strategy: try-catch

Validate before calling

fn storage_path_writable(p: &Path) -> Result<(), String> {
    if let Some(parent) = p.parent() {
        std::fs::create_dir_all(parent).map_err(|e| format!("mkdir: {e}"))?;
    }
    let probe = p.with_extension("write-probe");
    std::fs::write(&probe, b"").map_err(|e| format!("not writable: {e}"))?;
    std::fs::remove_file(&probe).map_err(|e| format!("cleanup: {e}"))?;
    Ok(())
} // also ensure no other app instance is running and holds the DB lock

Type guard

null

Try / catch

let storage = match Storage::try_new(&storage_path) {
    Ok(s) => s,
    Err(e) => {
        eprintln!("close running Nyanpasu instances and check permissions at {}: {e}", storage_path.display());
        return Err(anyhow::anyhow!("failed to open storage: {e}"));
    }
};

Prevention

When it happens

Trigger: Storage::try_new fails because the storage directory cannot be used: parent dir creation succeeded but the DB file is corrupt, locked by another process (a running app instance holds it), permissions deny access, or the path is invalid/on a full disk.

Common situations: Nyanpasu still running and holding the storage lock during migration; corrupted KV database from a previous crash; read-only install directory or antivirus blocking file creation; network/roaming profile paths unavailable.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/ca4a3df37b5801d9. Report an issue: GitHub.