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
- Run the Duplicati repair command to rebuild FileLookup/FilesetEntry integrity before purging volumes.
- If repair cannot fix it, recreate the local database from the remote backend.
- Investigate the count: SELECT COUNT(*) FROM "FilesetEntry" WHERE "FileID" NOT IN (SELECT "ID" FROM "FileLookup").
- 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
- Run VerifyConsistency before purging volumes.
- Never interrupt a backup/purge mid-way; if you do, repair before further operations.
- Keep the database on reliable storage.
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
- Detected {nonAttachedFiles} file(s) in FilesetEntry without
- Name was null
- Detected files associated with non-existing blocksets!
- Detected 1 fileset with missing volume: FilesetId = {reader.
- Detected {filesetsMissingVolumes} filesets with missing volu
AI-assisted analysis of duplicati/duplicati@3f348be3e3 (2026-08-13).
Data as JSON: /api/errors/4bce1bdbaa2630df.
Report an issue: GitHub.