{"record":{"id":"e59d2ed45083db3c","repo":"janhq/jan","slug":"database-error-0","errorCode":null,"errorMessage":"Database error: {0}","messagePattern":"Database error: (.+?)","errorType":"exception","errorClass":"VectorDBError","httpStatus":null,"severity":"error","filePath":"src-tauri/plugins/tauri-plugin-vector-db/src/error.rs","lineNumber":5,"sourceCode":"use serde::{Deserialize, Serialize};\n\n#[derive(Debug, thiserror::Error, Serialize, Deserialize)]\npub enum VectorDBError {\n    #[error(\"Database error: {0}\")]\n    DatabaseError(String),\n\n    #[error(\"Invalid input: {0}\")]\n    InvalidInput(String),\n}\n\nimpl From<rusqlite::Error> for VectorDBError {\n    fn from(err: rusqlite::Error) -> Self {\n        VectorDBError::DatabaseError(err.to_string())\n    }\n}\n\nimpl From<serde_json::Error> for VectorDBError {\n    fn from(err: serde_json::Error) -> Self {\n        VectorDBError::DatabaseError(err.to_string())\n    }\n}\n","sourceCodeStart":1,"sourceCodeEnd":23,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/src-tauri/plugins/tauri-plugin-vector-db/src/error.rs#L1-L23","documentation":"`VectorDBError::DatabaseError(String)` wraps a `rusqlite::Error` via the `From` impl — the wrapped string is `err.to_string()`. It fires for any SQLite-level failure: SQL syntax error, constraint violation, busy/locked database, corrupt database file, or a type coercion failure. The string carries the SQLite error code and message.","triggerScenarios":"Executing malformed SQL; inserting a row that violates a UNIQUE constraint; concurrent writes causing SQLITE_BUSY; a schema migration that fails halfway; a database file that is corrupt or locked by another process; binding a parameter of the wrong type.","commonSituations":"Two threads writing without a connection pool/WAL causing SQLITE_BUSY; a UNIQUE collision when re-inserting embeddings; a schema drift after a plugin upgrade; antivirus or backup tools locking the `.db` file on Windows; running off a network filesystem that does not support SQLite locking.","solutions":["Read the wrapped message — SQLite names the specific error (e.g. `database is locked`, `UNIQUE constraint failed`, `no such table`).","For SQLITE_BUSY, enable WAL mode and use a connection pool with a busy timeout (`PRAGMA busy_timeout`).","For constraint violations, deduplicate before insert (e.g. INSERT OR IGNORE / OR REPLACE).","For schema drift, run migrations on startup and confirm the expected schema version."],"exampleFix":"// before\nconn.execute(\"INSERT INTO embeddings (...) VALUES (...)\", params![...])?;\n\n// after - WAL + busy timeout + idempotent insert\nconn.execute_batch(\"PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;\")?;\nconn.execute(\"INSERT OR IGNORE INTO embeddings (...) VALUES (...)\", params![...])?;","handlingStrategy":"try-catch","validationCode":"fn db_wal_busy(path: &str) -> Result<Connection, rusqlite::Error> {\n    let conn = Connection::open(path)?;\n    conn.execute_batch(\"PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;\")?;\n    Ok(conn)\n}","typeGuard":"null","tryCatchPattern":"match conn.execute(sql, params) {\n    Ok(n) => Ok(n),\n    Err(rusqlite::Error::SqliteFailure(err, msg)) if err.code == rusqlite::ErrorCode::DatabaseBusy => {\n        tracing::warn!(\"db busy, retrying once\");\n        // single bounded retry, not a poll loop\n        conn.execute(sql, params).map_err(VectorDBError::from)\n    }\n    Err(e) => Err(e.into()),\n}","preventionTips":["Enable WAL mode and set a busy_timeout on every connection.","Use a single-writer connection pool to avoid SQLITE_BUSY under concurrency.","Run migrations on startup and assert the expected schema version.","Use INSERT OR IGNORE / OR REPLACE for idempotent embedding inserts."],"tags":["vector-db","sqlite","rusqlite","database","tauri","rust"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}