Zackriya-Solutions/meetily · error
Failed to start transaction: {}
Error message
Failed to start transaction: {} What it means
`sqlx::Connection::begin` failed to start the write transaction — SQLite refused BEGIN or could not take the write lock. The classic cause is SQLITE_BUSY/SQLITE_LOCKED: another connection (live recording writer, summary job, second app instance) holds the database write lock longer than busy_timeout allows. Note acquire() on the line above already succeeded, so the connection itself is healthy.
Source
Thrown at frontend/src-tauri/src/audio/import.rs:703
);
}
/// 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))?;
// Insert transcripts
for segment in segments {
sqlx::query(View on GitHub (pinned to 0281737d87)
Solutions
- Enable WAL and busy_timeout on connect (PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;) so BEGIN waits instead of failing
- Retry BEGIN on SQLITE_BUSY with short backoff
- Avoid running imports concurrently with other heavy DB write paths — the ImportGuard blocks concurrent imports, not other writers
- Move the DB off cloud-synced or network folders
Example fix
// before
let mut tx = sqlx::Connection::begin(&mut *conn).await
.map_err(|e| anyhow!("Failed to start transaction: {}", e))?;
// after — configure WAL + busy_timeout once, at pool creation
SqlitePoolOptions::new()
.max_connections(5)
.after_connect(|conn, _| Box::pin(async move {
sqlx::query("PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;")
.execute(&mut *conn)
.await?;
Ok(())
})) Defensive patterns
Strategy: retry
Validate before calling
-- verify journal mode once, at startup PRAGMA journal_mode; -- should report 'wal'
Try / catch
Retry Connection::begin twice with 250-500ms backoff when the sqlx error string contains 'database is locked'; give up after that and report contention rather than retrying forever.
Prevention
- Enable WAL journal and busy_timeout on every connection at pool creation
- Do not import while a live meeting is being recorded/saved
- Keep the DB off cloud-synced and network folders
When it happens
Trigger: Calling create_meeting_with_transcripts while another pooled connection is mid-write; the database in rollback-journal mode with concurrent readers blocking the writer; DB file on a locked or cloud-synced location.
Common situations: Importing while a live meeting recording is being saved; two app instances pointing at the same DB; the DB directory under OneDrive/Dropbox sync holding advisory locks.
Related errors
- Failed to commit transaction: {}
- DB error: {}
- Failed to create meeting: {}
- Failed to insert transcript: {}
- Failed to start transaction: {}
AI-assisted analysis of Zackriya-Solutions/meetily@0281737d87 (2026-08-16).
Data as JSON: /api/errors/1c8572082f307dce.
Report an issue: GitHub.