Zackriya-Solutions/meetily · error · anyhow::Error

Failed to delete existing transcripts: {}

Error message

Failed to delete existing transcripts: {}

What it means

The DELETE FROM transcripts WHERE meeting_id = ? executed inside the new transaction failed. Because a transaction was already started successfully, the likely causes are schema-level (transcripts table or meeting_id column missing after a skipped migration) or lock/I-O problems on the underlying file, not pool acquisition.

Source

Thrown at frontend/src-tauri/src/audio/retranscription.rs:440

    let segments = create_transcript_segments(&all_transcripts);

    // Save to database
    let app_state = app
        .try_state::<AppState>()
        .ok_or_else(|| anyhow!("App state not available"))?;

    // Wrap delete+insert+update in a transaction to prevent data loss
    let pool = app_state.db_manager.pool();
    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))?;

    sqlx::query("DELETE FROM transcripts WHERE meeting_id = ?")
        .bind(&meeting_id)
        .execute(&mut *tx)
        .await
        .map_err(|e| anyhow!("Failed to delete existing transcripts: {}", e))?;

    for segment in &segments {
        sqlx::query(
            "INSERT INTO transcripts (id, meeting_id, transcript, timestamp, audio_start_time, audio_end_time, duration)
             VALUES (?, ?, ?, ?, ?, ?, ?)"
        )
        .bind(&segment.id)
        .bind(&meeting_id)
        .bind(&segment.text)
        .bind(&segment.timestamp)
        .bind(segment.audio_start_time)
        .bind(segment.audio_end_time)
        .bind(segment.duration)
        .execute(&mut *tx)
        .await
        .map_err(|e| anyhow!("Failed to insert transcript: {}", e))?;
    }

View on GitHub (pinned to 0281737d87)

Solutions

  1. Read the inner error: 'no such table: transcripts' means the schema is missing - run app migrations or recreate the DB.
  2. 'database is locked' means another writer interfered - retry when idle.
  3. Run PRAGMA integrity_check on the database file to rule out corruption.
  4. Restart the app so the setup/migration path runs again.
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the schema exists before starting retranscription
async fn transcripts_table_exists(pool: &SqlitePool) -> bool {
    sqlx::query_scalar::<_, i64>(
        "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='transcripts'"
    ).fetch_one(pool).await.unwrap_or(0) > 0
}

Try / catch

// Distinguish schema errors (fix data) from lock errors (retry)
match delete_result {
    Err(e) if e.to_string().contains("no such table") => return Err(anyhow!("schema out of date - run migrations")),
    Err(e) if is_busy(&e) => retry_after_idle(),
    Err(e) => return Err(anyhow!("delete failed: {e}")),
    Ok(_) => {}
}

Prevention

When it happens

Trigger: Database created by an older app version whose migrations never produced the transcripts table; a schema where meeting_id was renamed; DB file moved or unmounted between BEGIN and DELETE.

Common situations: App upgraded but migrations did not run; user pointed the app at an old data directory; partially interrupted first-run initialization left a half-built schema.

Related errors


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