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

  1. Restore DB health and retry the upload
  2. Enable WAL / reduce concurrent writers for SQLite
  3. Apply migrations and verify the attachments table schema
  4. 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

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


AI-assisted analysis of dani-garcia/vaultwarden@0cefa4cca7 (2026-08-16). Data as JSON: /api/errors/752e522692af6b93. Report an issue: GitHub.