libnyanpasu/clash-nyanpasu · error
failed to save hotkeys: {e}
Error message
failed to save hotkeys: {e} What it means
The storage opened successfully, but Storage::set_item("hotkeys", &hotkey_strings) failed while persisting the migrated hotkeys. This is a write failure inside the KV storage backend (serialization of the value or the underlying DB write), and it aborts the migration leaving config.yaml unmodified.
Source
Thrown at backend/tauri/src/core/migration/modules/storage.rs:104
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(())
}
}
fn current_revision() -> u64 {View on GitHub (pinned to f7dbce2997)
Solutions
- Free disk space / resolve I/O errors on the volume holding the storage path, then re-run migration.
- Check {e}: if it indicates serialization failure, verify the hotkey_strings values are plain strings and the storage crate versions match.
- Restart after closing other app instances to rule out lock contention, retrying the migration.
- If the DB is corrupt, back up and remove the storage file so a fresh one is created (config.yaml remains the source of truth and can be re-migrated).
Example fix
// before
storage.set_item("hotkeys", &hotkey_strings)
.map_err(|e| anyhow::anyhow!("failed to save hotkeys: {e}"))?;
// after
storage.set_item("hotkeys", &hotkey_strings)
.with_context(|| "failed to save hotkeys to KV storage — check disk space and storage DB integrity")?; Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
if let Err(e) = storage.set_item("hotkeys", &hotkey_strings) {
eprintln!("KV write failed, config.yaml untouched — check disk space/DB integrity: {e}");
return Err(anyhow::anyhow!("failed to save hotkeys: {e}"));
} // safe to retry after remediation since the source file was not modified Prevention
- Ensure sufficient free disk space before running migrations
- Verify hotkey values are plain strings compatible with the storage serializer
- Keep storage backend crate versions aligned between writer and reader
- Back up the KV database before migration and verify the write by reading the item back afterwards
When it happens
Trigger: set_item errors because the value cannot be serialized into the storage format, the DB write fails mid-transaction (disk full, I/O error), the storage handle became invalid, or the file was locked/deleted after open by an external process.
Common situations: Disk-full or quota exceeded on the app-data volume; antivirus interfering after file creation; corrupt DB page surfaces on write; storage backend version mismatch on the serialized value format.
Related errors
- failed to open storage: {e}
- failed to parse config: {e}
- result.error
- cannot repair typed clash config before split_legacy_config
- unrecognized typed config migration state: existing {} is ne
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/2a3b4c2ced436723.
Report an issue: GitHub.