dani-garcia/vaultwarden · error
Error saving attachment
Error message
Error saving attachment
What it means
Start of the v2 attachment flow (POST /ciphers/{uuid}/attachment/v2): after write-access checks and file_size parsing, a new Attachment row is inserted with attachment.save(&conn).await.expect("Error saving attachment") — any DB failure panics the request before file bytes are transferred, so the client can simply retry once the DB is healthy.
Source
Thrown at src/api/core/ciphers.rs:1158
) -> JsonResult {
let Some(cipher) = Cipher::find_by_uuid(&cipher_id, &conn).await else {
err!("Cipher doesn't exist")
};
if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn).await {
err!("Cipher is not write accessible")
}
let data: AttachmentRequestData = data.into_inner();
let file_size = data.file_size.into_i64()?;
if file_size < 0 {
err!("Attachment size can't be negative")
}
let attachment_id = crypto::generate_attachment_id();
let attachment =
Attachment::new(attachment_id.clone(), cipher.uuid.clone(), data.file_name, file_size, Some(data.key));
attachment.save(&conn).await.expect("Error saving attachment");
let url = format!("/ciphers/{}/attachment/{attachment_id}", cipher.uuid);
let response_key = match data.admin_request {
Some(b) if b => "cipherMiniResponse",
_ => "cipherResponse",
};
Ok(Json(json!({ // AttachmentUploadDataResponseModel
"object": "attachment-fileUpload",
"attachmentId": attachment_id,
"url": url,
"fileUploadType": FileUploadType::Direct as i32,
response_key: cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &conn).await?,
})))
}
#[derive(FromForm)]
struct UploadData<'f> {View on GitHub (pinned to 0cefa4cca7)
Solutions
- Restore DB health and retry the upload
- Enable WAL / reduce concurrent writers for SQLite
- Apply migrations and verify the attachments table schema
- Code fix: propagate the save error instead of expect
Example fix
// before
attachment.save(&conn).await.expect("Error saving attachment");
// after
attachment.save(&conn).await.map_err(|e| Error::new("Failed to save attachment", e.to_string()))?; Defensive patterns
Strategy: try-catch
Validate before calling
# Confirm the attachments table is reachable before mass uploads sqlite3 /data/db.sqlite3 "SELECT count(*) FROM attachments;" && echo attachments-table-ok
Try / catch
match attachment.save(&conn).await {
Ok(_) => {}
Err(e) => {
error!("Failed to save attachment {}: {e}", attachment.id);
return Err(Error::new("Failed to save attachment", e.to_string()));
}
} Prevention
- Keep DB writes non-contended during bulk uploads
- Back up before mass upload operations
- Watch logs for repeated save failures indicating systemic DB problems
When it happens
Trigger: Starting an attachment upload from the web vault/CLI when the attachment insert fails: locked SQLite, dead connection, constraint violation, or full disk.
Common situations: Concurrent uploads on SQLite without WAL; DB failover mid-session; migrations not applied after an upgrade; batch imports creating many attachments at once.
Related errors
- Error updating attachment
- Error saving API key
- Grantee user should exist but does not!
- Unable to update emergency access status
- Grantor user not found
AI-assisted analysis of dani-garcia/vaultwarden@0cefa4cca7 (2026-08-16).
Data as JSON: /api/errors/752e522692af6b93.
Report an issue: GitHub.