t8y2/dbx · error
Failed to migrate JSON data
Error message
Failed to migrate JSON data
What it means
After opening storage, the app runs s.migrate_from_json(&data_dir) to import legacy JSON files into the new database and panics if migration fails. Failures stem from malformed or unreadable legacy JSON files or database write errors during import.
Solutions
- Back up and move the offending JSON files out of the data dir so the app can start with an empty database.
- Inspect the legacy JSON files with a validator to find malformed entries and fix or remove them.
- Downgrade to the previous app version to export clean data, or handle the Result with a skip-and-log policy for bad records instead of expect().
Example fix
// before
s.migrate_from_json(&data_dir).await.expect("Failed to migrate JSON data");
// after
if let Err(e) = s.migrate_from_json(&data_dir).await {
eprintln!("[STARTUP] JSON migration failed (continuing with empty db): {e}");
} Defensive patterns
Strategy: try-catch
Validate before calling
// Rust: validate legacy JSON before migrating
fn json_files_parse(dir: &std::path::Path) -> Vec<std::path::PathBuf> {
std::fs::read_dir(dir).into_iter().flatten()
.filter_map(|e| e.ok().map(|e| e.path()))
.filter(|p| p.extension().map_or(false, |x| x == "json"))
.filter(|p| std::fs::read_to_string(p)
.map(|s| serde_json::from_str::<serde_json::Value>(&s).is_err())
.unwrap_or(true))
.collect()
} Try / catch
match s.migrate_from_json(&data_dir).await {
Ok(()) => {}
Err(e) => eprintln!("[STARTUP] JSON migration failed, continuing: {e}"),
} Prevention
- Validate legacy JSON files (schema version + serde parse) before migrating.
- Make migration resumable/idempotent so an interrupted run can complete.
- Log and skip bad records instead of failing the whole migration; archive unparseable files.
When it happens
Trigger: migrate_from_json at lib.rs:1514 failing when legacy JSON files in the data dir are corrupt, have unsupported schema versions, or the newly opened storage rejects writes (permissions, constraints, disk full).
Common situations: Users upgrading from the JSON-based version with hand-edited or partially synced JSON files; interrupted previous migration leaving partial state; JSON encoding incompatibilities after a version change.
Related errors
- Failed to migrate JSON data
- Failed to open storage
- invalid_json
- is required
- TDengine database is required
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/e40fa4036f167adc.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/src/lib.rs:1514
let data_dir = data_dir_resolution.data_dir.clone();
std::fs::create_dir_all(&data_dir).expect("Failed to create data dir");
let data_dir_mode = startup_data_dir_mode(&data_dir_resolution.mode);
append_startup_probe(format!("data dir ready mode={data_dir_mode}"));
let alternative_data_dir = data_dir::alternative_data_dir(&data_dir_resolution);
match maybe_import_user_data_db(&data_dir, alternative_data_dir.as_deref()) {
Ok(result) => eprintln!("[STARTUP] data db fallback import: {result:?}"),
Err(err) => eprintln!("[STARTUP] data db fallback import failed: {err}"),
}
let db_path = data_dir.join("dbx.db");
let t = Instant::now();
append_startup_probe(format!("opening storage file=dbx.db data_dir_mode={data_dir_mode}"));
let storage = tauri::async_runtime::block_on(async {
let s = Storage::open(&db_path).await.expect("Failed to open storage");
eprintln!("[STARTUP] Storage::open in {:?}", t.elapsed());
append_startup_probe(format!("storage opened in {:?}", t.elapsed()));
let t2 = Instant::now();
s.migrate_from_json(&data_dir).await.expect("Failed to migrate JSON data");
eprintln!("[STARTUP] migrate_from_json in {:?}", t2.elapsed());
append_startup_probe(format!("json migration completed in {:?}", t2.elapsed()));
s
});
let desktop_settings = tauri::async_runtime::block_on(storage.load_desktop_settings()).unwrap_or_default();
app.handle().plugin(
tauri_plugin_log::Builder::default()
.timezone_strategy(tauri_plugin_log::TimezoneStrategy::UseLocal)
.format(|out, message, record| {
out.finish(format_args!(
"[{}][{}][{}] {}",
chrono::Local::now().format("%Y-%m-%d][%H:%M:%S%.3f"),
record.level(),
record.target(),
message
));
})
.level(log::LevelFilter::Debug)View on GitHub (pinned to c0390bff16)