spacedriveapp/spacedrive · error · anyhow::Error

Expected TransferFinalAck, got different message type

Error message

Expected TransferFinalAck, got different message type

What it means

The acknowledgment decoded successfully but was not TransferFinalAck - most commonly the peer sent TransferError instead, meaning its post-receive verification (size or checksum) failed. A late control message arriving before the ack produces the same error. The job fails after the entire file was already transmitted.

Source

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

	let mut len_buf = [0u8; 4];
	recv_stream.read_exact(&mut len_buf).await?;
	let msg_len = u32::from_be_bytes(len_buf) as usize;

	let mut msg_buf = vec![0u8; msg_len];
	recv_stream.read_exact(&mut msg_buf).await?;

	let ack_message: crate::service::network::protocol::file_transfer::FileTransferMessage =
		rmp_serde::from_slice(&msg_buf)?;

	match ack_message {
		crate::service::network::protocol::file_transfer::FileTransferMessage::TransferFinalAck { transfer_id: ack_id } => {
			if ack_id != transfer_id {
				return Err(anyhow::anyhow!("Received ack for wrong transfer: expected {}, got {}", transfer_id, ack_id));
			}
			ctx.log("Received TransferFinalAck from receiver - transfer confirmed!".to_string());
		}
		_ => {
			return Err(anyhow::anyhow!("Expected TransferFinalAck, got different message type"));
		}
	}

	// Only mark completed after receiver confirms to detect network failures.
	file_transfer_protocol.update_session_state(
		&transfer_id,
		crate::service::network::protocol::file_transfer::TransferState::Completed,
	)?;

	ctx.log(format!(
		"File streaming completed and acknowledged: {} chunks, {} bytes sent to device {}",
		chunk_index, bytes_transferred, destination_device_id
	));

	// Signal file completion to aggregator
	if let Some(callback) = progress_callback {
		callback(bytes_transferred, u64::MAX);
	}

View on GitHub (pinned to 6dfeccf211)

Solutions

  1. If the message is a TransferError, inspect its contents for the receiver-side failure (usually checksum mismatch) and check disk health on the receiver
  2. Check free disk space on the receiving device
  3. Retry the transfer after addressing the receiver-side cause
  4. Align sd-core versions on both devices
Defensive patterns

Strategy: try-catch

Try / catch

match ack_message {
    FileTransferMessage::TransferFinalAck { transfer_id } if transfer_id == expected => { /* confirmed */ }
    FileTransferMessage::TransferError { message, .. } => {
        // Receiver verified and rejected; retrying blindly repeats the failure.
        anyhow::bail!("Receiver rejected transfer: {}", message);
    }
    other => anyhow::bail!("Unexpected final message: {:?}", other),
}

Prevention

When it happens

Trigger: Receiver-side checksum or size verification fails after all chunks arrive, so it answers with TransferError; protocol version differences emit a different final message; an unexpected control message interleaves before the ack.

Common situations: Receiver disk truncating writes, receiver running different verification logic after an upgrade, memory pressure causing partial writes on the receiver.

Related errors


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