spacedriveapp/spacedrive · error · anyhow::Error

Failed to generate content hash

Error message

Failed to generate content hash: {}

What it means

calculate_file_checksum wraps a failure from ContentHashGenerator::generate_content_hash, invoked when verify_checksum is enabled for a copy to compute the local file's checksum for integrity verification. The underlying cause is almost always filesystem-level: the file could not be opened or read to completion. The anyhow context adds no new information beyond naming the hashing step.

Solutions

  1. Verify the file exists and the daemon user can read it at the logged path
  2. Retry once - transient exclusive locks (antivirus scans) usually clear within seconds
  3. Add library roots to antivirus real-time scanning exclusion lists
  4. If integrity verification is optional for this workflow, run the job with verify_checksum disabled

Example fix

// before
let checksum = calculate_file_checksum(&path).await?;

// after - degrade gracefully when verification is optional
let checksum = match calculate_file_checksum(&path).await {
    Ok(c) => Some(c),
    Err(e) if !require_verification => {
        tracing::warn!(error = %e, "checksum verify skipped");
        None
    }
    Err(e) => return Err(e),
};
Defensive patterns

Strategy: retry

Validate before calling

// Verify the file is readable before paying for a hash pass.
let f = tokio::fs::File::open(&path).await
    .with_context(|| format!("cannot open for hashing: {}", path.display()))?;
drop(f);

Try / catch

// Transient read locks (antivirus, other indexers) clear quickly; retry once before failing.
let hash = match calculate_file_checksum(&path).await {
    Ok(h) => h,
    Err(first) => {
        tokio::time::sleep(std::time::Duration::from_secs(2)).await;
        calculate_file_checksum(&path).await.map_err(|_| first)?
    }
};

Prevention

When it happens

Trigger: Calling copy with verify_checksum=true; between transfer completion and hashing the file is deleted, moved, exclusively locked by another process, or the daemon loses read permission; hashing a file truncated by an earlier interrupted write.

Common situations: Antivirus or another indexer holding exclusive locks on Windows; file consumed and deleted by an application immediately after arrival; permissions changed by a sync tool.

Related errors


AI-assisted analysis of spacedriveapp/spacedrive@6dfeccf211 (2026-08-16). Data as JSON: /api/errors/e318f6e0a6346e92. Report an issue: GitHub.

Appendix: source

Thrown at core/src/ops/files/copy/strategy.rs:1081

	let file_size = metadata.len();
	copy_single_file(
		source,
		destination,
		volume_info,
		ctx,
		verify_checksum,
		file_size,
		progress_callback,
	)
	.await
}

/// Calculate file checksum for integrity verification
async fn calculate_file_checksum(path: &Path) -> Result<String> {
	crate::domain::content_identity::ContentHashGenerator::generate_content_hash(path)
		.await
		.map_err(|e| anyhow::anyhow!("Failed to generate content hash: {}", e))
}

/// Stream file data in chunks to the remote device using a persistent connection
async fn stream_file_data<'a>(
	file_path: &Path,
	transfer_id: uuid::Uuid,
	file_transfer_protocol: &crate::service::network::protocol::FileTransferProtocolHandler,
	total_size: u64,
	destination_device_id: uuid::Uuid,
	destination_path: String,
	file_metadata: crate::service::network::protocol::FileMetadata,
	ctx: &JobContext<'a>,
	progress_callback: Option<&ProgressCallback<'a>>,
) -> Result<()> {
	use blake3::Hasher;
	use tokio::io::{AsyncReadExt, AsyncWriteExt};

	debug!(

View on GitHub (pinned to 6dfeccf211)