LykosAI/StabilityMatrix · error · IOException

Failed to delete junction point

Error message

Failed to delete junction point {directory.FullPath}

What it means

DeleteVerbose wraps directory delete failures in an IOException naming the junction point path. The directory.Delete(false) call failed with an IOException (non-fatal OS error like in-use, permissions, or reparse-point issues). It rethrows with the full path preserved as the message so the caller knows exactly which junction failed.

Solutions

  1. Close processes holding handles to the junction or its target (check with handle.exe/lsof), then retry the delete
  2. Verify the process has write/delete permission on the junction and its parent directory
  3. Check the inner exception for the exact OS error and whether the junction target still exists
  4. As a fallback, remove the junction explicitly with fsutil reparsepoint delete (Windows) before deleting the directory

Example fix

// before
directory.Delete(false);
// after
// release handles / remove reparse point first
if (directory.LinkTarget is not null)
{
    Process.Start("fsutil", $"reparsepoint delete \"{directory.FullPath}\"")?.WaitForExit();
}
directory.Delete(false);
Defensive patterns

Strategy: try-catch

Validate before calling

if (Directory.Exists(path))
{
    var di = new DirectoryInfo(path);
    if (di.Attributes.HasFlag(FileAttributes.ReparsePoint))
        Console.WriteLine($"Junction at {path} -> {di.LinkTarget}; ensure target is not in use");
}

Type guard

static bool IsJunction(DirectoryInfo d) =>
    d.Attributes.HasFlag(FileAttributes.ReparsePoint) && d.LinkTarget is not null;

Try / catch

try { DirectoryPathExtensions.DeleteVerbose(dir, logger, ct); }
catch (IOException ex) when (ex.InnerException is not null)
{
    logger.LogWarning(ex, "Delete failed for {Path}; retry after releasing handles", dir);
    // retry once after delay or surface to user
}

Prevention

When it happens

Trigger: DeleteVerbose/DeleteVerboseAsync encounters an IOException when calling directory.Delete(false) on a junction point directory, e.g. the junction target is locked, a handle is open, or the reparse point cannot be removed.

Common situations: Deleting an installed package directory that contains symlinks/junctions while the target (e.g. a running ComfyUI venv or a mounted model folder) is in use; antivirus or a shell holding the junction open; partial cleanup after a failed delete pass left the junction in a bad state.

Related errors


AI-assisted analysis of LykosAI/StabilityMatrix@af93d6ef57 (2026-09-12). Data as JSON: /api/errors/3cee58870c4e4aef. Report an issue: GitHub.

Appendix: source

Thrown at StabilityMatrix.Core/Extensions/DirectoryPathExtensions.cs:72

        cancellationToken.ThrowIfCancellationRequested();

        // Skip if directory does not exist
        if (!directory.Exists)
        {
            return;
        }
        // For junction points, delete with recursive false
        if (directory.IsSymbolicLink)
        {
            logger?.LogInformation("Removing junction point {TargetDirectory}", directory.FullPath);
            try
            {
                directory.Delete(false);
                return;
            }
            catch (IOException ex)
            {
                throw new IOException($"Failed to delete junction point {directory.FullPath}", ex);
            }
        }
        // Recursively delete all subdirectories
        foreach (var subDir in directory.EnumerateDirectories())
        {
            DeleteVerbose(subDir, logger, cancellationToken);
        }

        // Delete all files in the directory
        foreach (var filePath in directory.EnumerateFiles())
        {
            cancellationToken.ThrowIfCancellationRequested();

            try
            {
                filePath.Info.Attributes = FileAttributes.Normal;
                filePath.Delete();
            }

View on GitHub (pinned to af93d6ef57)