Summary
The shared Roslyn worker for execute-dynamic-code fails to start when the system has .NET 6 installed alongside Unity 6000.4.x (which bundles .NET 8). The fallback to one-shot compilation works, but the shared worker is unavailable.
Error Messages
[uLoopMCP] execute-dynamic-code shared Roslyn worker failed to operate correctly
[uLoopMCP] execute-dynamic-code shared Roslyn worker is unavailable; falling back to one-shot compiler execution
Underlying exception (from Editor.log):
Unhandled exception. System.IO.FileLoadException: Could not load file or assembly 'System.Console, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The located assembly's manifest definition does not match the assembly reference. (0x80131040)
File name: 'System.Console, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
at Program.Main()
Root Cause Analysis
The issue is a mismatch between the .NET version used to compile the worker and the .NET version used to run it:
-
Worker compilation — BuildWorkerReferenceSet() in SharedRoslynCompilerWorkerHost.cs picks references from NetCoreRuntimeSharedDirectoryPath, which resolves to Unity's bundled runtime directory. For Unity 6000.4.1f1, this is .../NetCoreRuntime/shared/Microsoft.NETCore.App/8.0.16/. So the worker assembly is compiled against .NET 8 references → System.Console, Version=8.0.0.0.
-
Worker execution — The worker is launched with --runtimeconfig csc.runtimeconfig.json, which targets net6.0 / 6.0.21 with rollForwardOnNoCandidateFx: 2.
-
Runtime selection — Unity's dotnet.exe searches both its own bundled runtime directory and the system-wide .NET installation (C:\Program Files\dotnet\shared\). Because the system has .NET 6.0.36 installed, the host finds a compatible .NET 6 runtime and uses it — without rolling forward to .NET 8.
-
Crash — The worker runs on .NET 6, but its assembly references System.Console, Version=8.0.0.0 (a .NET 8 assembly). No binding redirect exists to satisfy this, causing the FileLoadException.
If .NET 6 were not installed system-wide, the host would find no .NET 6 runtime and roll forward to Unity's bundled .NET 8 — and the worker would start successfully.
Suggested Fix
In SharedRoslynCompilerWorkerHost.cs, when launching the worker process, pass DOTNET_MULTILEVEL_LOOKUP=0 as an environment variable to prevent the dotnet host from probing the system-wide installation. This forces it to use only Unity's bundled runtime, ensuring the runtime version matches the compiled references.
Alternatively, generate or select a runtimeconfig.json that targets the same .NET version as the references used for compilation (i.e., detect Unity's bundled runtime version and target that).
Environment
| Item |
Value |
| OS |
Windows 10 Home (10.0.19045) |
| Unity |
6000.4.1f1 |
| Unity bundled .NET runtime |
8.0.16 |
| uLoopMCP |
latest (https://github.com/hatayama/unity-cli-loop.git) |
| System .NET SDKs |
6.0.428, 8.0.206, 8.0.420 |
| System .NET Runtimes |
6.0.36, 8.0.6, 8.0.26 |
Impact
execute-dynamic-code shared worker is unavailable
- Fallback to one-shot compilation works correctly — all functionality is preserved, just slower per invocation
Summary
The shared Roslyn worker for
execute-dynamic-codefails to start when the system has .NET 6 installed alongside Unity 6000.4.x (which bundles .NET 8). The fallback to one-shot compilation works, but the shared worker is unavailable.Error Messages
Underlying exception (from
Editor.log):Root Cause Analysis
The issue is a mismatch between the .NET version used to compile the worker and the .NET version used to run it:
Worker compilation —
BuildWorkerReferenceSet()inSharedRoslynCompilerWorkerHost.cspicks references fromNetCoreRuntimeSharedDirectoryPath, which resolves to Unity's bundled runtime directory. For Unity 6000.4.1f1, this is.../NetCoreRuntime/shared/Microsoft.NETCore.App/8.0.16/. So the worker assembly is compiled against .NET 8 references →System.Console, Version=8.0.0.0.Worker execution — The worker is launched with
--runtimeconfig csc.runtimeconfig.json, which targetsnet6.0 / 6.0.21withrollForwardOnNoCandidateFx: 2.Runtime selection — Unity's
dotnet.exesearches both its own bundled runtime directory and the system-wide .NET installation (C:\Program Files\dotnet\shared\). Because the system has .NET 6.0.36 installed, the host finds a compatible .NET 6 runtime and uses it — without rolling forward to .NET 8.Crash — The worker runs on .NET 6, but its assembly references
System.Console, Version=8.0.0.0(a .NET 8 assembly). No binding redirect exists to satisfy this, causing theFileLoadException.If .NET 6 were not installed system-wide, the host would find no .NET 6 runtime and roll forward to Unity's bundled .NET 8 — and the worker would start successfully.
Suggested Fix
In
SharedRoslynCompilerWorkerHost.cs, when launching the worker process, passDOTNET_MULTILEVEL_LOOKUP=0as an environment variable to prevent the dotnet host from probing the system-wide installation. This forces it to use only Unity's bundled runtime, ensuring the runtime version matches the compiled references.Alternatively, generate or select a
runtimeconfig.jsonthat targets the same .NET version as the references used for compilation (i.e., detect Unity's bundled runtime version and target that).Environment
https://github.com/hatayama/unity-cli-loop.git)Impact
execute-dynamic-codeshared worker is unavailable