{"record":{"id":"fda94cee88810de8","repo":"spacedriveapp/spacedrive","slug":"checksum-verification-failed-source-dest","errorCode":null,"errorMessage":"Checksum verification failed: source={}, dest={}","messagePattern":"Checksum verification failed: source=(.+?), dest=(.+?)","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"core/src/ops/files/copy/strategy.rs","lineNumber":962,"sourceCode":"\tdest_file.flush().await?;\n\tdest_file.sync_all().await?;\n\n\tif let Some(callback) = progress_callback {\n\t\tcallback(file_size, u64::MAX);\n\t\tctx.log(format!(\n\t\t\t\"Strategy final progress: {} / {} bytes (100%)\",\n\t\t\ttotal_copied, file_size\n\t\t));\n\t}\n\n\tif verify_checksum {\n\t\tif let (Some(source_hasher), Some(dest_hasher)) = (source_hasher, dest_hasher) {\n\t\t\tlet source_hash = source_hasher.finalize();\n\t\t\tlet dest_hash = dest_hasher.finalize();\n\n\t\t\tif source_hash != dest_hash {\n\t\t\t\tlet _ = fs::remove_file(destination).await;\n\t\t\t\treturn Err(std::io::Error::new(\n\t\t\t\t\tstd::io::ErrorKind::InvalidData,\n\t\t\t\t\tformat!(\n\t\t\t\t\t\t\"Checksum verification failed: source={}, dest={}\",\n\t\t\t\t\t\tsource_hash.to_hex(),\n\t\t\t\t\t\tdest_hash.to_hex()\n\t\t\t\t\t),\n\t\t\t\t));\n\t\t\t}\n\n\t\t\tctx.log(format!(\n\t\t\t\t\"Checksum verification passed for {}: {}\",\n\t\t\t\tdestination.display(),\n\t\t\t\tsource_hash.to_hex()\n\t\t\t));\n\t\t}\n\t}\n\n\tlet source_metadata = fs::metadata(source).await?;","sourceCodeStart":944,"sourceCodeEnd":980,"githubUrl":"https://github.com/spacedriveapp/spacedrive/blob/6dfeccf2113039e35f2ce735f945e70dc3e4ea45/core/src/ops/files/copy/strategy.rs#L944-L980","documentation":"After a copy with verify_checksum enabled, the code finalizes two blake3 hashers (one fed from the source stream, one from the destination stream) and compares digests. A mismatch means the bytes written differ from the bytes read; the destination file is deleted and ErrorKind::InvalidData is returned with both hex digests for post-mortem correlation.","triggerScenarios":"Source file modified concurrently while being read (hash computed over a moving target); failing disk or flaky USB cable corrupting writes mid-transfer; destination on a buggy FUSE/network filesystem that alters data; oversized writes silently truncated; antivirus/backup agents touching the file between write and flush.","commonSituations":"Copying photos off a camera SD card while the camera is still mounted for writing; copying to a failing HDD or a saturated network share; exotic filesystems (exFAT on some kernels, network volumes) with write quirks.","solutions":["Retry the copy once: transient media and network glitches are the most common cause and the code already removed the bad destination","If it fails repeatedly on the same file, hash the source twice and compare: a changing digest means the source is being modified during the read, not the copy is broken","Check destination disk health (smartctl) and cabling, or copy to a different volume to isolate the failing side","Pause other writers (sync/index jobs, editors) that may touch the source during the copy"],"exampleFix":"// before: single attempt, mismatch fails the whole job\ncopy(...).await?;\n\n// after: retry once on checksum mismatch, then escalate\nfor attempt in 0..2 {\n    match copy(...).await {\n        Err(e) if e.kind() == std::io::ErrorKind::InvalidData && attempt == 0 => continue,\n        other => break other?,\n    }\n}","handlingStrategy":"retry","validationCode":"// Before a critical copy, confirm the source is quiescent (two reads, same digest)\nlet h1 = blake3::hash(&tokio::fs::read(src).await?);\ntokio::time::sleep(Duration::from_millis(250)).await;\nlet h2 = blake3::hash(&tokio::fs::read(src).await?);\nassert_eq!(h1, h2, \"source is being modified concurrently; copying now will fail verification\");","typeGuard":null,"tryCatchPattern":"match copy_result {\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidData && e.to_string().contains(\"Checksum\") => {\n        retry_with_backoff(/* once */).await // transient media glitch most of the time\n    }\n    other => other?,\n}","preventionTips":["Enable verify_checksum for any copy onto removable or network media","Quiesce the source (stop cameras/sync writers) before verified copies","Capture both digests from the message in logs: they identify which side changed"],"tags":["copy","checksum","blake3","integrity"],"backgroundTag":null,"analyzedSha":"6dfeccf2113039e35f2ce735f945e70dc3e4ea45","analyzedAt":"2026-08-16T11:26:17.074Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}