LykosAI/StabilityMatrix · error · IOException

Unable to delete junction point.

Error message

Unable to delete junction point.

What it means

Delete() first removes the reparse point via DeviceIoControl, then calls Directory.Delete(junctionPoint). If the final Directory.Delete throws IOException (directory not empty, locked, or on a deleted target), the method wraps it in this IOException. It means the junction reparse data was removed but the now-plain directory itself could not be deleted.

Solutions

  1. Check that the directory under the junction path is actually empty (e.g. Directory.EnumerateFileSystemEntries) before calling Delete; move out or delete any contents
  2. Close any process holding handles inside the junction (apps, editors, antivirus scans) and retry
  3. Call Junction.Exists(path) first to confirm it is still a junction; if the reparse point was already removed, use Directory.Delete directly
  4. Catch IOException, inspect the inner exception, and clean up the leftover empty/plain directory manually

Example fix

// before
Junction.Delete(path); // IOException if dir not empty
// after
if (Junction.Exists(path))
{
    foreach (var f in Directory.EnumerateFileSystemEntries(path))
        throw new IOException($"Junction target not empty: {f}");
    Junction.Delete(path);
}
else
{
    Directory.Delete(path, recursive: true);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!Directory.Exists(path)) throw new FileNotFoundException(path);
bool isJunction = Junction.Exists(path);
bool empty = !Directory.EnumerateFileSystemEntries(path).Any();
if (!isJunction || !empty) throw new InvalidOperationException("Path must be an empty junction point.");

Type guard

static bool IsDeletableJunction(string path) =>
    Directory.Exists(path) && Junction.Exists(path) && !Directory.EnumerateFileSystemEntries(path).Any();

Try / catch

try
{
    Junction.Delete(path);
}
catch (IOException ex)
{
    // inner exception tells whether dir was locked/not empty
    logger.LogError(ex.InnerException ?? ex, "Failed to delete junction {Path}", path);
    if (Directory.Exists(path) && !Junction.Exists(path))
        Directory.Delete(path, recursive: false); // reparse already removed
}

Prevention

When it happens

Trigger: Calling Junction.Delete on a path whose underlying directory contains files placed inside it after the junction was created, or where the directory handle is held open by another process, or where the target was removed concurrently so the directory state is inconsistent.

Common situations: Deleting a shared-models junction while an app (e.g. ComfyUI) still has files open inside it; antivirus or an indexer holding the directory; a partially-failed previous Delete attempt leaving a plain non-empty directory; running on a network drive where reparse deletion half-completed.

Related errors


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

Appendix: source

Thrown at StabilityMatrix.Core/ReparsePoints/Junction.cs:160

            Debug.WriteLine($"bytesReturned: {bytesReturned}");
            
            if (!result)
            {
                ThrowLastWin32Error($"Unable to delete junction point {junctionPoint}");
            }
        }
        finally
        {
            Marshal.FreeHGlobal(inBuffer);
        }

        try
        {
            Directory.Delete(junctionPoint);
        }
        catch (IOException ex)
        {
            throw new IOException("Unable to delete junction point.", ex);
        }
    }
    
    /// <summary>
    /// Determines whether the specified path exists and refers to a junction point.
    /// </summary>
    /// <param name="path">The junction point path</param>
    /// <returns>True if the specified path represents a junction point</returns>
    /// <exception cref="IOException">Thrown if the specified path is invalid
    /// or some other error occurs</exception>
    public static bool Exists(string path)
    {
        if (!Directory.Exists(path)) return false;

        using var handle = OpenReparsePoint(path, Win32FileAccess.GenericRead);
        var target = InternalGetTarget(handle);
        return target != null;
    }

View on GitHub (pinned to af93d6ef57)