{"record":{"id":"aa17b9c438abb38f","repo":"dani-garcia/vaultwarden","slug":"error-updating-attachment","errorCode":null,"errorMessage":"Error updating attachment","messagePattern":"Error updating attachment","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/api/core/ciphers.rs","lineNumber":1298,"sourceCode":"    if let Some(attachment) = &mut attachment {\n        // v2 API\n\n        // Check the actual size against the size initially provided by\n        // the client. Upstream allows +/- 1 MiB deviation from this\n        // size, but it's not clear when or why this is needed.\n        const LEEWAY: i64 = 1024 * 1024; // 1 MiB\n        let Some(max_size) = attachment.file_size.checked_add(LEEWAY) else {\n            err!(\"Invalid attachment size max\")\n        };\n        let Some(min_size) = attachment.file_size.checked_sub(LEEWAY) else {\n            err!(\"Invalid attachment size min\")\n        };\n\n        if min_size <= size && size <= max_size {\n            if size != attachment.file_size {\n                // Update the attachment with the actual file size.\n                attachment.file_size = size;\n                attachment.save(&conn).await.expect(\"Error updating attachment\");\n            }\n        } else {\n            attachment.delete(&conn).await.ok();\n\n            err!(format!(\"Attachment size mismatch (expected within [{min_size}, {max_size}], got {size})\"));\n        }\n    } else {\n        // Legacy API\n\n        // SAFETY: This value is only stored in the database and is not used to access the file system.\n        // As a result, the conditions specified by Rocket [0] are met and this is safe to use.\n        // [0]: https://docs.rs/rocket/latest/rocket/fs/struct.FileName.html#-danger-\n        let encrypted_filename = data.data.raw_name().map(|s| s.dangerous_unsafe_unsanitized_raw().to_string());\n\n        if encrypted_filename.is_none() {\n            err!(\"No filename provided\")\n        }\n        if data.key.is_none() {","sourceCodeStart":1280,"sourceCodeEnd":1316,"githubUrl":"https://github.com/dani-garcia/vaultwarden/blob/0cefa4cca7c9f2a5579dd290f78193b543818c51/src/api/core/ciphers.rs#L1280-L1316","documentation":"Completion of a direct attachment upload (POST /ciphers/{cipher_uuid}/attachments/{attachment_id}): when the streamed size differs from the declared size but stays within the ±1MiB leeway, the server rewrites attachment.file_size with .expect(\"Error updating attachment\") — a DB failure here panics after the file bytes were already stored, leaving metadata stale or the row missing.","triggerScenarios":"Finishing an upload where actual size != declared size (within 1MiB) and the update fails: locked DB, the row concurrently deleted, or connection loss at completion time.","commonSituations":"Flaky networks ending uploads with slightly different sizes; two clients finishing the same attachment id; DB maintenance exactly when uploads complete.","solutions":["Retry the upload from the start; remove the orphaned attachment record on the cipher if needed","Restore DB availability; confirm the attachments row still exists before retrying","Code fix: return a proper error instead of expect"],"exampleFix":"// before\nattachment.save(&conn).await.expect(\"Error updating attachment\");\n// after\nattachment.save(&conn).await.map_err(|e| Error::new(\"Failed to update attachment size\", e.to_string()))?;","handlingStrategy":"try-catch","validationCode":"-- Verify the pending attachment row still exists before finishing the upload\nSELECT id, file_size, akey FROM attachments WHERE id = '<attachment_id>';","typeGuard":null,"tryCatchPattern":"match attachment.save(&conn).await {\n    Ok(_) => {}\n    Err(e) => {\n        error!(\"Failed to update attachment size for {}: {e}\", attachment.id);\n        return Err(Error::new(\"Failed to update attachment size\", e.to_string()));\n    }\n}","preventionTips":["Declare accurate file sizes when starting v2 uploads","Do not delete ciphers/attachments while uploads are in flight","Keep the DB healthy during upload completion windows"],"tags":["rust","database","attachments","panic","file-upload"],"backgroundTag":null,"analyzedSha":"0cefa4cca7c9f2a5579dd290f78193b543818c51","analyzedAt":"2026-08-16T07:44:56.102Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}