Zackriya-Solutions/meetily · error

DB error: {}

Error message

DB error: {}

What it means

`pool.acquire()` failed before any SQL executed. sqlx surfaces two main kinds here: PoolTimedOut (every connection in the SQLite pool was checked out longer than acquire_timeout, default 30s) and PoolClosed (the pool was closed, typically during app shutdown while the import was still saving). The {} carries that underlying sqlx error.

Source

Thrown at frontend/src-tauri/src/audio/import.rs:700

            progress_percentage: progress,
            message: message.to_string(),
        },
    );
}


/// Create a new meeting with transcripts in the database
async fn create_meeting_with_transcripts(
    pool: &sqlx::SqlitePool,
    title: &str,
    segments: &[TranscriptSegment],
    folder_path: String,
) -> Result<String> {
    let meeting_id = format!("meeting-{}", Uuid::new_v4());
    let now = chrono::Utc::now();

    // Start transaction
    let mut conn = pool.acquire().await.map_err(|e| anyhow!("DB error: {}", e))?;
    let mut tx = sqlx::Connection::begin(&mut *conn)
        .await
        .map_err(|e| anyhow!("Failed to start transaction: {}", e))?;

    // Insert meeting
    sqlx::query(
        "INSERT INTO meetings (id, title, created_at, updated_at, folder_path)
         VALUES (?, ?, ?, ?, ?)",
    )
    .bind(&meeting_id)
    .bind(title)
    .bind(now)
    .bind(now)
    .bind(&folder_path)
    .execute(&mut *tx)
    .await
    .map_err(|e| anyhow!("Failed to create meeting: {}", e))?;

View on GitHub (pinned to 0281737d87)

Solutions

  1. Read the sqlx error kind in the {} — PoolTimedOut and PoolClosed have opposite fixes
  2. For PoolTimedOut: raise max_connections and acquire_timeout in the PoolOptions used by db_manager
  3. For PoolClosed: keep the import task alive until completion (await its handle before closing the pool at shutdown)
  4. Enable WAL journal and busy_timeout on connect so writers queue less

Example fix

// before
let mut conn = pool.acquire().await.map_err(|e| anyhow!("DB error: {}", e))?;

// after — retry transient pool exhaustion with backoff
let mut conn = loop {
    match pool.acquire().await {
        Ok(c) => break c,
        Err(e) if retries < 3 && e.to_string().contains("timed out") => {
            retries += 1;
            tokio::time::sleep(std::time::Duration::from_millis(500)).await;
        }
        Err(e) => return Err(anyhow!("DB error: {}", e)),
    }
};
Defensive patterns

Strategy: retry

Validate before calling

// size the pool for concurrent DB users at startup
SqlitePoolOptions::new()
    .max_connections(8)
    .acquire_timeout(Duration::from_secs(60))
    .connect(&db_url)
    .await?

Try / catch

Retry pool.acquire() up to 3 times with 500ms backoff when the sqlx error is a timeout; fail immediately on PoolClosed since retrying a closed pool never succeeds.

Prevention

When it happens

Trigger: The import reaches the 'saving' stage (85%) while other long-running DB operations hold all pool connections; the app quits and closes the pool mid-import; or the SQLite file/directory becomes unreadable so new connections fail.

Common situations: Importing while a meeting summary or history query runs; many concurrent Tauri commands sharing a small pool; app exit before async tasks finish; DB file permissions changed by backup software.

Related errors


AI-assisted analysis of Zackriya-Solutions/meetily@0281737d87 (2026-08-16). Data as JSON: /api/errors/791f139d54f2fd6b. Report an issue: GitHub.