Summary
When NuGetPackageRestorer.Restore() runs while package downloads fail (offline / registry unreachable), NuGetPackageInstaller.RemoveUnnecessaryPackages() still executes its removal sweep with an essentially empty InstalledThisSession keep-closure. Result: it deletes committed, previously-valid DLLs from Assets/Plugins/NuGet/ (case 2: "DLLs now provided by Unity ... and package ID not in the closure", NuGetPackageInstaller.cs:293-307) and strips their entries from .nuget-installed.json.
Observed 2026-06-05 in the ai-game-developer-infra Unity-Test-Project testbed (Unity 6000.5.0b10, plugin source at McpPlugin 6.7.0, committed DLLs at 6.5.2/ReflectorNet 5.2.0):
- All 14
Install() calls failed with [NuGet] Failed to download <pkg>: An error occurred while sending the request (cold Library = empty NuGetCache, no fallback).
- The sweep then deleted 14 DLLs (Microsoft.Bcl.AsyncInterfaces, MS.Extensions.*.Abstractions, System.Buffers/Memory/Text.Json/...) that the previous committed state depended on.
- The manifest is left in a state that permanently fails
AllPackagesInstalled() -> full restore re-runs on every domain reload (churn), and the deletions get reverted by the first ONLINE restore anyway - so the offline sweep is pure destruction with no steady state.
- The pass ends with
[NuGet] All packages up to date. because anyChanged == false after total failure - misleading when 14/14 downloads errored.
Proposed fix
- Guard the sweep: skip
RemoveUnnecessaryPackages (or at minimum its case 2) when any configured top-level Install() failed this session - the keep-closure is incomplete, so removal decisions are unsound.
- Do not log "All packages up to date." when installs failed; log a clear
restore incomplete: N package(s) failed to download warning instead.
Repro
- Commit a valid
Assets/Plugins/NuGet state for plugin version X.
- Bump plugin source to version Y (newer deps).
- Delete
Library/, block network access to nuget registries, open the project.
- Observe: compile errors (expected), then the resolver deletes previously-committed DLLs and reports "All packages up to date."
🤖 Generated with Claude Code
Summary
When
NuGetPackageRestorer.Restore()runs while package downloads fail (offline / registry unreachable),NuGetPackageInstaller.RemoveUnnecessaryPackages()still executes its removal sweep with an essentially emptyInstalledThisSessionkeep-closure. Result: it deletes committed, previously-valid DLLs fromAssets/Plugins/NuGet/(case 2: "DLLs now provided by Unity ... and package ID not in the closure", NuGetPackageInstaller.cs:293-307) and strips their entries from.nuget-installed.json.Observed 2026-06-05 in the ai-game-developer-infra Unity-Test-Project testbed (Unity 6000.5.0b10, plugin source at McpPlugin 6.7.0, committed DLLs at 6.5.2/ReflectorNet 5.2.0):
Install()calls failed with[NuGet] Failed to download <pkg>: An error occurred while sending the request(cold Library = empty NuGetCache, no fallback).AllPackagesInstalled()-> full restore re-runs on every domain reload (churn), and the deletions get reverted by the first ONLINE restore anyway - so the offline sweep is pure destruction with no steady state.[NuGet] All packages up to date.becauseanyChanged == falseafter total failure - misleading when 14/14 downloads errored.Proposed fix
RemoveUnnecessaryPackages(or at minimum its case 2) when any configured top-levelInstall()failed this session - the keep-closure is incomplete, so removal decisions are unsound.restore incomplete: N package(s) failed to downloadwarning instead.Repro
Assets/Plugins/NuGetstate for plugin version X.Library/, block network access to nuget registries, open the project.🤖 Generated with Claude Code