duplicati/duplicati · error · Exception

Failed to move block {blockkey}:{size} from volume {sourcevo

Error message

Failed to move block {blockkey}:{size} from volume {sourcevolumeid}, count: {c}

What it means

Generic Exception in MoveBlockToVolumeAsync: an UPDATE setting VolumeID on Block rows matching Hash+Size+PreviousVolumeId returned != 1 affected row. Exactly one row should match; 0 means no block matches that triple (moved already, or wrong ids), >1 means duplicate blocks — both are integrity/concurrency problems.

Source

Thrown at Duplicati/Library/Main/Database/Local/LocalBackupDatabase.cs:1821

        {
            await using var cmd = m_connection.CreateCommand(m_rtr);
            var c = await cmd.SetCommandAndParameters(@"
                    UPDATE ""Block""
                    SET ""VolumeID"" = @NewVolumeId
                    WHERE
                        ""Hash"" = @Hash
                        AND ""Size"" = @Size
                        AND ""VolumeID"" = @PreviousVolumeId
                    ")
                .SetParameterValue("@NewVolumeId", targetvolumeid)
                .SetParameterValue("@Hash", blockkey)
                .SetParameterValue("@Size", size)
                .SetParameterValue("@PreviousVolumeId", sourcevolumeid)
                .ExecuteNonQueryAsync(true, token)
                .ConfigureAwait(false);

            if (c != 1)
                throw new Exception($"Failed to move block {blockkey}:{size} from volume {sourcevolumeid}, count: {c}");
        }

        /// <summary>
        /// Safely deletes a remote volume by checking if it has any associated blocks.
        /// If it does, an exception is thrown; otherwise, the volume is removed.
        /// </summary>
        /// <param name="name">The name of the remote volume to delete.</param>
        /// <param name="token">The cancellation token to cancel the operation.</param>
        /// <returns>A task that completes when the remote volume is safely deleted.</returns>
        /// <exception cref="Exception">Thrown if the volume has associated blocks.</exception>
        public async Task SafeDeleteRemoteVolumeAsync(string name, CancellationToken token)
        {
            var volumeid = await GetRemoteVolumeIDAsync(name, token).ConfigureAwait(false);

            await using var cmd = m_connection.CreateCommand(m_rtr);
            var c = await cmd.SetCommandAndParameters(@"
                    SELECT COUNT(*)
                    FROM ""Block""

View on GitHub (pinned to 3f348be3e3)

Solutions

  1. Verify the block actually still resides on sourcevolumeid before moving (avoid re-running moves).
  2. Serialize compaction/purge operations against the local database.
  3. Run database repair to deduplicate/reconcile Block rows.
  4. Recreate the local db from the remote store if corruption persists.
Defensive patterns

Strategy: validation

Validate before calling

// Optional: confirm the block is still on the source volume before moving:
// (uses the same lookup the update performs; if absent, skip the move)

Try / catch

try { await db.MoveBlockToVolumeAsync(blockkey, size, srcVol, tgtVol, token); }
catch (Exception ex) when (ex.Message.Contains("Failed to move block"))
{ Log.Warn("Block move mismatch; block may already be relocated. Serialize jobs and run repair."); throw; }

Prevention

When it happens

Trigger: Call MoveBlockToVolumeAsync(blockkey, size, sourcevolumeid, targetvolumeid) where the UPDATE matches 0 or >1 Block rows.

Common situations: Block already moved (re-running a compaction/purge), concurrent operation racing on the same block, duplicate Block rows with identical Hash+Size+VolumeID (corruption), caller passing stale/incorrect volume ids.

Related errors


AI-assisted analysis of duplicati/duplicati@3f348be3e3 (2026-08-13). Data as JSON: /api/errors/26e134d69a8a8f7c. Report an issue: GitHub.