{"record":{"id":"c8a998b8965801c9","repo":"Zackriya-Solutions/meetily","slug":"failed-to-insert-transcript-c8a998","errorCode":null,"errorMessage":"Failed to insert transcript: {}","messagePattern":"Failed to insert transcript: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"frontend/src-tauri/src/audio/retranscription.rs","lineNumber":456,"sourceCode":"        .execute(&mut *tx)\n        .await\n        .map_err(|e| anyhow!(\"Failed to delete existing transcripts: {}\", e))?;\n\n    for segment in &segments {\n        sqlx::query(\n            \"INSERT INTO transcripts (id, meeting_id, transcript, timestamp, audio_start_time, audio_end_time, duration)\n             VALUES (?, ?, ?, ?, ?, ?, ?)\"\n        )\n        .bind(&segment.id)\n        .bind(&meeting_id)\n        .bind(&segment.text)\n        .bind(&segment.timestamp)\n        .bind(segment.audio_start_time)\n        .bind(segment.audio_end_time)\n        .bind(segment.duration)\n        .execute(&mut *tx)\n        .await\n        .map_err(|e| anyhow!(\"Failed to insert transcript: {}\", e))?;\n    }\n\n    tx.commit().await\n        .map_err(|e| anyhow!(\"Failed to commit transaction: {}\", e))?;\n\n    info!(\n        \"Updated {} transcripts for meeting {} in transaction\",\n        segments.len(),\n        meeting_id\n    );\n\n    // Write updated transcripts.json and metadata.json to the meeting folder\n    emit_progress(&app, &meeting_id, \"saving\", 90, \"Writing transcript files...\");\n\n    if let Err(e) = write_transcripts_json(&folder_path, &segments) {\n        warn!(\"Failed to write transcripts.json: {}\", e);\n    }\n","sourceCodeStart":438,"sourceCodeEnd":474,"githubUrl":"https://github.com/Zackriya-Solutions/meetily/blob/0281737d87d26352fb0adc78c8c0975f691b23d1/frontend/src-tauri/src/audio/retranscription.rs#L438-L474","documentation":"One of the INSERT statements for the new transcript segments failed, aborting the save. Because delete and inserts share one transaction, the whole batch rolls back and the meeting keeps its previous transcripts - that rollback is the intended data-loss guard. The inner '{}' usually names a constraint: NOT NULL violation (empty text, NULL timestamp/duration), a CHECK/FK violation, or a type mismatch in a bound column.","triggerScenarios":"A segment whose text is empty or whose audio_start_time/audio_end_time/duration bound as NULL against a NOT NULL column; schema drift where struct fields no longer match table columns (for example duration column added by a newer migration); a duplicate primary key id.","commonSituations":"An older database file used with a newer app build that inserts new columns; create_transcript_segments producing an empty-text row through an edge case; timestamp math yielding NaN for a zero-length segment.","solutions":["Read the inner error - it names the exact failed constraint or column.","Log the offending segment's id/text/timestamps when this error fires to identify which field is bad.","Filter and normalize segments before insert: skip empty text, clamp non-finite timestamps and durations to defaults.","If the error is a schema mismatch, run the app's migrations so transcripts matches the INSERT shape."],"exampleFix":"// before: insert every segment blindly\nfor segment in &segments {\n    sqlx::query(\"INSERT INTO transcripts ...\")...\n}\n\n// after: skip segments that cannot satisfy constraints\nfor segment in &segments {\n    if segment.text.trim().is_empty()\n        || !segment.audio_start_time.map_or(false, |v| v.is_finite())\n    {\n        warn!(\"Skipping invalid transcript segment {}\");\n        continue;\n    }\n    sqlx::query(\"INSERT INTO transcripts ...\")...\n}","handlingStrategy":"validation","validationCode":"// Validate every segment against the table's constraints before the transaction\nfn segment_is_insertable(s: &TranscriptSegment) -> bool {\n    !s.text.trim().is_empty()\n        && !s.id.is_empty()\n        && s.timestamp.is_finite()\n        && s.audio_start_time.map_or(false, |v| v.is_finite())\n        && s.audio_end_time.map_or(false, |v| v.is_finite())\n}","typeGuard":null,"tryCatchPattern":"// Log the offending row so the failing column is identifiable\nif let Err(e) = insert_result {\n    error!(\"insert failed for segment id={} text_len={}: {e}\", segment.id, segment.text.len());\n    return Err(anyhow!(\"Failed to insert transcript: {e}\"));\n}","preventionTips":["Filter empty-text and non-finite-timestamp segments before insert.","Keep struct fields and table columns in sync via migrations whenever the INSERT shape changes.","Rely on the surrounding transaction: a failed insert rolls back the delete, so old data survives."],"tags":["sqlx","sqlite","insert","constraint-violation"],"backgroundTag":"sql-insert-constraint-violation","analyzedSha":"0281737d87d26352fb0adc78c8c0975f691b23d1","analyzedAt":"2026-08-16T20:57:52.567Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}