t8y2/dbx · critical

Failed to open storage

Error message

Failed to open storage

What it means

Panics/aborts in dbx-web main during startup when opening the application storage (database under DBX_DATA_DIR) fails — the storage layer could not be initialized (unwritable data directory, locked database file, or corrupted store). The web server refuses to start without working persistence.

Solutions

  1. Check that no other dbx-web process is using dbx.db and that the file/directory is writable.
  2. Verify disk space and inode availability on the data volume.
  3. If the database is corrupted, restore from backup or move dbx.db aside to re-initialize (losing local data).
  4. Run migrations/schema fixes from the same or newer dbx version; avoid downgrading across schema changes.
  5. Wrap Storage::open in error handling that logs the underlying SQLite error instead of a bare panic.

Example fix

// before
let storage = Storage::open(&db_path).await.expect("Failed to open storage");
// after
let storage = Storage::open(&db_path)
    .await
    .unwrap_or_else(|e| panic!("Failed to open storage at {}: {e}", db_path.display()));
Defensive patterns

Strategy: try-catch

Validate before calling

let db_path = data_dir.join("dbx.db");
let db_ok = std::path::Path::new(&db_path).map(|p| !p.exists() || p.metadata().map(|m| m.is_file()).unwrap_or(false)).unwrap_or(false)
    && data_dir_writable(&data_dir);

Try / catch

let storage = match Storage::open(&db_path).await {
    Ok(s) => s,
    Err(e) => { eprintln!("Failed to open storage {}: {e}", db_path.display()); std::process::exit(1); }
};

Prevention

When it happens

Trigger: Storage::open(&db_path) fails when the data directory is unwritable, the SQLite file is corrupted or locked, disk is full, or the db file format is incompatible with the current rusqlite/sqlx version.

Common situations: Corrupted dbx.db after a crash or disk-full event; another dbx-web instance holding the SQLite lock; wrong ownership on a mounted volume; upgrading across an incompatible database schema version.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/94b855b13564d12f. Report an issue: GitHub.

Appendix: source

Thrown at crates/dbx-web/src/main.rs:304

    tracing_subscriber::fmt()
        .with_env_filter(
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| "dbx_web=info,tower_http=info".parse().unwrap()),
        )
        .init();

    rustls::crypto::aws_lc_rs::default_provider().install_default().expect("Failed to install rustls crypto provider");

    // Data directory
    let data_dir = std::env::var("DBX_DATA_DIR").map(std::path::PathBuf::from).unwrap_or_else(|_| {
        let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
        std::path::PathBuf::from(home).join(".dbx-web")
    });
    std::fs::create_dir_all(&data_dir).expect("Failed to create data directory");

    let app_state = {
        let db_path = data_dir.join("dbx.db");
        let storage = Storage::open(&db_path).await.expect("Failed to open storage");
        storage.migrate_from_json(&data_dir).await.expect("Failed to migrate JSON data");

        // Initialize core dialect registry and load external plugin dialects
        register_core_dialects();
        let registry = DialectRegistry::global();
        let plugin_dirs = vec![data_dir.join("plugins").join("dialects")];
        let load_result = DialectPluginLoader::scan_and_load(registry, &plugin_dirs);
        log::info!(
            "Dialect plugins loaded: {} success, {} errors, {} skipped",
            load_result.loaded.len(),
            load_result.errors.len(),
            load_result.skipped.len()
        );

        // Start dialect YAML hot-reload watcher
        let watch_dirs = plugin_dirs.clone();
        tokio::spawn(async move {
            if let Err(e) = DialectHotReload::run_forever(watch_dirs, DialectRegistry::global()).await {

View on GitHub (pinned to c0390bff16)