Skip to content

[Bug] Shared Roslyn worker fails when system .NET 6 is installed alongside Unity 6000.4.x (which bundles .NET 8) #985

@L1247

Description

@L1247

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:

  1. Worker compilationBuildWorkerReferenceSet() 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.

  2. Worker execution — The worker is launched with --runtimeconfig csc.runtimeconfig.json, which targets net6.0 / 6.0.21 with rollForwardOnNoCandidateFx: 2.

  3. 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.

  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions