duplicati/duplicati · error · ConstraintException

Refusing to remove remote volumes, detected {nonAttachedFile

Error message

Refusing to remove remote volumes, detected {nonAttachedFilesPre} file(s) in FilesetEntry without corresponding FileLookup entry

What it means

Thrown by RemoveRemoteVolumesAsync as a pre-condition guard: it counts FilesetEntry rows whose FileID has no matching ID in FileLookup, and refuses to proceed if any exist. Removing volumes cascades block/blockset deletions, so orphaned FilesetEntry rows would compound corruption; the method aborts before mutating. A non-zero count indicates referential-integrity breakage that must be fixed first.

Source

Thrown at Duplicati/Library/Main/Database/Local/LocalDatabase.cs:1035

        /// <returns>A task that completes when the remote volumes have been removed.</returns>
        public async Task RemoveRemoteVolumesAsync(IEnumerable<string> names, CancellationToken token)
        {
            if (names == null || !names.Any()) return;

            await using var deletecmd = m_connection.CreateCommand(m_rtr);

            var nonAttachedFilesPre = await deletecmd.ExecuteScalarInt64Async(@"
                SELECT COUNT(*)
                FROM ""FilesetEntry""
                WHERE ""FileID"" NOT IN (
                    SELECT ""ID""
                    FROM ""FileLookup""
                )
            ", token)
                .ConfigureAwait(false);

            if (nonAttachedFilesPre > 0)
                throw new ConstraintException($"Refusing to remove remote volumes, detected {nonAttachedFilesPre} file(s) in FilesetEntry without corresponding FileLookup entry");


            string temptransguid = Library.Utility.Utility.GetHexGuid();
            var volidstable = $"DelVolSetIds-{temptransguid}";
            var blocksetidstable = $"DelBlockSetIds-{temptransguid}";
            var filesetidstable = $"DelFilesetIds-{temptransguid}";

            // Create and fill a temp table with the volids to delete. We avoid using too many parameters that way.
            await deletecmd.ExecuteNonQueryAsync($@"
                CREATE TEMP TABLE ""{volidstable}"" (
                    ""ID"" INTEGER PRIMARY KEY
                )
            ", token)
                .ConfigureAwait(false);

            await using var tmptable = await TemporaryDbValueList.CreateAsync(this, names, token)
                .ConfigureAwait(false);

View on GitHub (pinned to 3f348be3e3)

Solutions

  1. Run the Duplicati repair command to rebuild FileLookup/FilesetEntry integrity before purging volumes.
  2. If repair cannot fix it, recreate the local database from the remote backend.
  3. Investigate the count: SELECT COUNT(*) FROM "FilesetEntry" WHERE "FileID" NOT IN (SELECT "ID" FROM "FileLookup").
  4. Restore the local database from a known-good copy and retry the purge.
Defensive patterns

Strategy: validation

Validate before calling

var orphaned = await cmd.ExecuteScalarInt64Async("SELECT COUNT(*) FROM \"FilesetEntry\" WHERE \"FileID\" NOT IN (SELECT \"ID\" FROM \"FileLookup\")", token);
if (orphaned > 0) /* run repair before removing volumes */

Try / catch

try { await db.RemoveRemoteVolumeAsync(name, token); }
catch (ConstraintException ex) when (ex.Message.Contains("Refusing to remove remote volumes")) { /* repair first, then retry */ }

Prevention

When it happens

Trigger: Calling RemoveRemoteVolumeAsync / RemoveRemoteVolumesAsync on a database where FilesetEntry references FileIDs absent from FileLookup. This precedes the volume-deletion logic (temp tables, blockset cleanup).

Common situations: Database corruption from a crash mid-backup. Interrupted prior volume removal that left dangling FilesetEntry rows. Manual edits or partial restore of the database. Schema/invariant violation surfaced during purge.

Related errors


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