Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

can not run csharp lsp if net 9 installed even i installed net8,Please allow rollforword #7925

Closed
heartacker opened this issue Jan 14, 2025 · 13 comments

Comments

@heartacker
Copy link
Contributor

heartacker commented Jan 14, 2025

Environment data

dotnet --info output:

.NET SDK:
 Version:           9.0.100
 Commit:            a2bc464e40
 Workload version:  9.0.100-manifests.6bf02610
 MSBuild version:   17.12.7+a2bc464e4

Runtime Environment:
 OS Name:     ubuntu
 OS Version:  24.10
 OS Platform: Linux
 RID:         ubuntu.24.10-x64
 Base Path:   /usr/lib/dotnet/sdk/9.0.100/

.NET workloads installed:
There are no installed workloads to display.
Configured to use loose manifests when installing new manifests.

Host:
  Version:      9.0.0
  Architecture: x64
  Commit:       a2bc464e40

.NET SDKs installed:
  8.0.111 [/usr/lib/dotnet/sdk]
  9.0.100 [/usr/lib/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 8.0.11 [/usr/lib/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 9.0.0 [/usr/lib/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 8.0.11 [/usr/lib/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 9.0.0 [/usr/lib/dotnet/shared/Microsoft.NETCore.App]

Other architectures found:
  None

Environment variables:
  Not set

global.json file:
  Not found

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download

VS Code version:

版本: 1.96.3
提交: 91fbdddc47bc9c09064bf7acf133d22631cbf083
日期: 2025-01-09T18:14:09.060Z
浏览器: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Code/1.96.3 Chrome/128.0.6613.186 Electron/32.2.6 Safari/537.36

C# Extension version:

名称: C#
ID: ms-dotnettools.csharp
说明: Base language support for C#
版本: 2.61.28
发布者: Microsoft
VS Marketplace 链接: https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp

OmniSharp log

2025-01-14 13:52:30.964 [info] Locating .NET runtime version 8.0.10
2025-01-14 13:52:31.421 [info] Dotnet path: /usr/bin/dotnet
2025-01-14 13:52:31.421 [info] Activating C# + C# Dev Kit + C# IntelliCode...
2025-01-14 13:52:42.637 [info] Language server process exited with 131
2025-01-14 13:52:42.650 [info] [Error - 1:52:42 PM] Microsoft.CodeAnalysis.LanguageServer client: couldn't create connection to server.
2025-01-14 13:52:42.650 [info] Error: Language server process exited unexpectedly
    at ChildProcess.<anonymous> (/home/acker/.vscode-server/extensions/ms-dotnettools.csharp-2.61.28-linux-x64/dist/extension.js:2:1177399)
    at ChildProcess.emit (node:events:530:35)
    at ChildProcess.emit (node:domain:489:12)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:293:12)
2025-01-14 13:52:42.676 [error] [stderr] You must install .NET to run this application.

App: /home/acker/.vscode-server/extensions/ms-dotnettools.csharp-2.61.28-linux-x64/.roslyn/Microsoft.CodeAnalysis.LanguageServer
Architecture: x64
App host version: 8.0.11
.NET location: Not found

Learn more:
https://aka.ms/dotnet/app-launch-failed

Download the .NET runtime:
https://aka.ms/dotnet-core-applaunch?missing_runtime=true&arch=x64&rid=linux-x64&os=ubuntu.24.10&apphost_version=8.0.11


Steps to reproduce

install net 9 and no net 8

Expected behavior

work well

Actual behavior

Additional context

please allow language server runing net 9 and above within --allow-roll-forward

see https://learn.microsoft.com/zh-cn/dotnet/core/tools/dotnet#rollforward

@heartacker heartacker changed the title can not run without net8 even i install net9,Please allow rollforword can not run csharp lsp if net 9 installed even i installed net8,Please allow rollforword Jan 14, 2025
@dibarbet
Copy link
Member

I suspect there is something wrong with your .NET installation. We do enable "rollForward": "Major" in our runtime config:

{
  "runtimeOptions": {
    "tfm": "net8.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "8.0.0"
    },
    "rollForward": "Major",
    "configProperties": {
      "System.GC.Server": true,
      "System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
      "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
    }
  }
}

I tested this myself with only a .NET 9 installation and everything seems to work as expected

.NET SDK:
 Version:           9.0.102
 Commit:            cb83cd4923
 Workload version:  9.0.100-manifests.43af17c7
 MSBuild version:   17.12.18+ed8c6aec5

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.26100
 OS Platform: Windows
 RID:         win-x64
 Base Path:   C:\Program Files\dotnet\sdk\9.0.102\

.NET workloads installed:
There are no installed workloads to display.
Configured to use loose manifests when installing new manifests.

Host:
  Version:      9.0.1
  Architecture: x64
  Commit:       c8acea2262

.NET SDKs installed:
  9.0.102 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 9.0.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 9.0.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 9.0.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found:
  None

Environment variables:
  Not set

global.json file:
  Not found

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download

In your dotnet --info output I'm seeing both .NET 9 and .NET 8 at /usr/lib/dotnet. The extension seems to be picking up .NET from /usr/bin/dotnet. Is /usr/bin/dotnet symlinked to /usr/lib/dotnet or is that a different installation? Or do you by chance have dotnetAcquisitionExtension.existingDotnetPath set to look at /usr/bin/dotnet instead?

@heartacker
Copy link
Contributor Author

i set dotnetAcquisitionExtension.existingDotnetPath in my config, But that shouldn't be a problem, right?

@GuilhermeAlecrim7K
Copy link

I'm having the exact same issue with the same version of the lsp extension. I went back one version to 2.55.29 and it works fine. It is either a bug with this version or a breaking change.

@dibarbet
Copy link
Member

i set dotnetAcquisitionExtension.existingDotnetPath in my config, But that shouldn't be a problem, right?

A couple things

  1. Can you try removing it to see if that works?
  2. What is the value set to, and if you call --info on that path, what does it output?
  3. And what does /usr/lib/dotnet --info show?

It seems like something is pointing the extension to a path that doesn't have a completely valid .NET

@GuilhermeAlecrim7K
Copy link

In your dotnet --info output I'm seeing both .NET 9 and .NET 8 at /usr/lib/dotnet. The extension seems to be picking up .NET from /usr/bin/dotnet. Is /usr/bin/dotnet symlinked to /usr/lib/dotnet or is that a different installation? Or do you by chance have dotnetAcquisitionExtension.existingDotnetPath set to look at /usr/bin/dotnet instead?

In my case /usr/bin/dotnet is a symlink to the executable at /usr/lib/dotnet/dotnet. I believe it was set up by the package manager during installation so that I wouldn't have to add usr/lib/dotnet to PATH. I changed the dotnetAcquisitionExtension.existingDotnetPath setting to /usr/lib/dotnet and it fixed the issue on version 2.61.28.

PS: My dotnet info

.NET SDK:
 Version:           8.0.112
 Commit:            3f0c4a16e5
 Workload version:  8.0.100-manifests.784cc8f7

Runtime Environment:
 OS Name:     pop
 OS Version:  22.04
 OS Platform: Linux
 RID:         ubuntu.22.04-x64
 Base Path:   /usr/lib/dotnet/sdk/8.0.112/

.NET workloads installed:
 Workload version: 8.0.100-manifests.784cc8f7
There are no installed workloads to display.

Host:
  Version:      8.0.12
  Architecture: x64
  Commit:       89ef51c5d8

.NET SDKs installed:
  7.0.119 [/usr/lib/dotnet/sdk]
  8.0.112 [/usr/lib/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 7.0.19 [/usr/lib/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 8.0.12 [/usr/lib/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 7.0.19 [/usr/lib/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.12 [/usr/lib/dotnet/shared/Microsoft.NETCore.App]

Other architectures found:
  None

Environment variables:
  Not set

global.json file:
  Not found

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download

@dibarbet
Copy link
Member

Thanks for the info!

I changed the dotnetAcquisitionExtension.existingDotnetPath setting to /usr/lib/dotnet and it fixed the issue on version 2.61.28.

So I think you shouldn't necessarily need to even need to have dotnetAcquisitionExtension.existingDotnetPath if its just pointing to the same .NET install thats on your path. You should be able to clear it out at this point.

However - @nagilson should the runtime extension be returning the real path if dotnetAcquisitionExtension.existingDotnetPath is set to a symlink? It looks like it currently returns the symlink, and when we set DOTNET_ROOT to the symlink, the server fails to start with the missing dotnet error listed above.

@zeroskyx
Copy link

I can confirm that the issue occurs when path for extension ms-dotnettools.csharp dotnetAcquisitionExtension.existingDotnetPath is pointing to a symlink.

Changing it to the actual binary solves the problem.

@nagilson
Copy link
Member

It currently returns the symlink, but it does verify if the PATH meets the conditions of the API request, however, by scanning the runtime and SDK by trying to call the symlink as an executable /usr/lib/dotnet --list-runtimes, --info, etc. If it doesn't meet the requirement then it will move on to the PATH, and if that doesn't work it will try the realpath on the PATH.

@dibarbet How does the server host fail if calling dotnet on the host path has the runtime and sdk that's on the request? (Presumably.) It would be good to collect a log from us from the user too if the answer to that isn't immediately obvious. Maybe this wasn't as noticeable as we also do the --list-runtimes logic on the PATH to go 2 directories up for the PATH but we dont do that on the user setting, which might be the better way to solve this.

@dibarbet
Copy link
Member

@dibarbet How does the server host fail if calling dotnet on the host path has the runtime and sdk that's on the request?

To clarify - it looks like the server is failing because we're trying to find the DOTNET_ROOT from the executable path you give back to us. When the option points to a symlink, the executable path we get is the symlink path. Then because DOTNET_ROOT requires a folder, we attempt to get the folder from the executable, which for a symlink isn't correct - https://github.com/dotnet/vscode-csharp/blob/main/src/lsptoolshost/dotnetRuntimeExtensionResolver.ts#L80

Image

To me it seems like the symlink should be resolved before we get the executable path? Open to ideas though. We could also resolve it, but seems possibly not ideal since we don't own the option.

@nagilson
Copy link
Member

Snap (after installing via package manager and uninstalling, then using snap)

Interesting, the symlink which could be set as /usr/lib/dotnet in this case doesn't work as an executable with snap when we try to find the executable due to how its placed (/usr/lib/dotnet/dotnet does not exist.)

But our code uses custom snap logic to make it work in a different method.

Package Manager

However with a package manager, /usr/lib/dotnet is the actual location and /usr/lib/dotnet/dotnet is a valid executable, so this works. /usr/bin/dotnet is also a valid executable, and that is the symlink to the lib folder, so our logic accepts it, but then it's not valid for DOTNET_ROOT. I didnt know DOTNET_ROOT required the folder, so thank you for that context @dibarbet.

Our path logic tries to not do realpath first, and this would normally have been caught so we'd give the correct path; its OK because we call --list-runtimes and go 2 directories up. But we didn't do that for the user setting path in an attempt to respect what they set.

Solution

This should be fixed in our extension.
I dont know if I'd call it a 'bug' per se, but we can make the behavior better. They shouldnt have to know all of this nuance.

This means we could
a - call realpath on /usr/bin/dotnet (or the user setting)
b - call --list-runtimes on /usr/bin/dotnet (the user setting) and go 2 directories up to find the actual path

both would cause us to return path.join(/usr/lib/dotnet, dotnet), however, realpath already returns the executable, while list--runtimes parsing would point us only to /usr/lib/dotnet/shared/blah.

a and b have similar ish performance characteristics because they both need to spawn a shell (most expensive perf), but --list-runtimes is going to be cached in the future so that theoretically means list-runtimes is faster as it wont need to spawn a shell. I guess we could cache realpath too, so performance isnt much of a concern.

b is better in my opinion, as a would actually break with a snap install. realpath of snap/bin/dotnet is /usr/bin/snap, which is not a valid executable, so it wouldn't be found. since it follows the flow of the other code in our extension to go 2 dirs up at the end of the call. Users do some (wild) stuff and if they removed realpath from their system we'd also be cooked in that situation, not that its a high priority.

@nagilson
Copy link
Member

I'll make a PR to fix this. Thank you for reporting and I'm sorry for this inconvenience.

nagilson added a commit to nagilson/vscode-dotnet-runtime that referenced this issue Jan 24, 2025
For dotnet/vscode-csharp#7925 (comment)

We have a custom setting and could cause C# to fail if someone sets the setting to a symlink install which doesn't actually have a folder in it, even if it has a valid executable because DOTNET_ROOT wants a folder. Read more below on how this fix works and why.

# Snap (after installing via package manager and uninstalling, then using snap)

Interesting, the symlink which could be set as /usr/lib/dotnet in this case doesn't work as an executable with snap when we try to find the executable due to how its placed (/usr/lib/dotnet/dotnet does not exist.)

But our code uses custom snap logic to make it work in a different method.

# Package Manager
However with a package manager, /usr/lib/dotnet is the actual location and /usr/lib/dotnet/dotnet is a valid executable, so this works. /usr/bin/dotnet is also a valid executable, and that is the symlink to the lib folder, so our logic accepts it, but then it's not valid for DOTNET_ROOT. I didnt know DOTNET_ROOT required the folder, so thank you for that context @dibarbet.

Our path logic tries to not do realpath first, and this would normally have been caught so we'd give the correct path; its OK because we call --list-runtimes and go 2 directories up. But we didn't do that for the user setting path in an attempt to respect what they set.

# Solution

This should be fixed in our extension.
I dont know if I'd call it a 'bug' per se, but we can make the behavior better. They shouldnt have to know all of this nuance.

This means we could
a - call realpath on /usr/bin/dotnet (or the user setting)
b - call --list-runtimes on /usr/bin/dotnet (the user setting) and go 2 directories up to find the actual path

both would cause us to return path.join(/usr/lib/dotnet, dotnet), however, realpath already returns the executable, while list--runtimes parsing would point us only to /usr/lib/dotnet/shared/blah.

a and b have similar ish performance characteristics because they both need to spawn a shell (most expensive perf), but --list-runtimes is going to be cached in the future so that theoretically means list-runtimes is faster as it wont need to spawn a shell. I guess we could cache realpath too, so performance isnt much of a concern.

b is better in my opinion, as a would actually break with a snap install. realpath of snap/bin/dotnet is /usr/bin/snap, which is not a valid executable, so it wouldn't be found. since it follows the flow of the other code in our extension to go 2 dirs up at the end of the call. Users do some (wild) stuff and if they removed realpath from their system we'd also be cooked in that situation, not that its a high priority.
nagilson added a commit to dotnet/vscode-dotnet-runtime that referenced this issue Jan 28, 2025
* Permit Invalid Symlink Folders to Work as Custom Path Setting

For dotnet/vscode-csharp#7925 (comment)

We have a custom setting and could cause C# to fail if someone sets the setting to a symlink install which doesn't actually have a folder in it, even if it has a valid executable because DOTNET_ROOT wants a folder. Read more below on how this fix works and why.

# Snap (after installing via package manager and uninstalling, then using snap)

Interesting, the symlink which could be set as /usr/lib/dotnet in this case doesn't work as an executable with snap when we try to find the executable due to how its placed (/usr/lib/dotnet/dotnet does not exist.)

But our code uses custom snap logic to make it work in a different method.

# Package Manager
However with a package manager, /usr/lib/dotnet is the actual location and /usr/lib/dotnet/dotnet is a valid executable, so this works. /usr/bin/dotnet is also a valid executable, and that is the symlink to the lib folder, so our logic accepts it, but then it's not valid for DOTNET_ROOT. I didnt know DOTNET_ROOT required the folder, so thank you for that context @dibarbet.

Our path logic tries to not do realpath first, and this would normally have been caught so we'd give the correct path; its OK because we call --list-runtimes and go 2 directories up. But we didn't do that for the user setting path in an attempt to respect what they set.

# Solution

This should be fixed in our extension.
I dont know if I'd call it a 'bug' per se, but we can make the behavior better. They shouldnt have to know all of this nuance.

This means we could
a - call realpath on /usr/bin/dotnet (or the user setting)
b - call --list-runtimes on /usr/bin/dotnet (the user setting) and go 2 directories up to find the actual path

both would cause us to return path.join(/usr/lib/dotnet, dotnet), however, realpath already returns the executable, while list--runtimes parsing would point us only to /usr/lib/dotnet/shared/blah.

a and b have similar ish performance characteristics because they both need to spawn a shell (most expensive perf), but --list-runtimes is going to be cached in the future so that theoretically means list-runtimes is faster as it wont need to spawn a shell. I guess we could cache realpath too, so performance isnt much of a concern.

b is better in my opinion, as a would actually break with a snap install. realpath of snap/bin/dotnet is /usr/bin/snap, which is not a valid executable, so it wouldn't be found. since it follows the flow of the other code in our extension to go 2 dirs up at the end of the call. Users do some (wild) stuff and if they removed realpath from their system we'd also be cooked in that situation, not that its a high priority.

* Try to get the existing path

* Only call real path if allow invalid path is false

* Fix the test

* Improve test logic
@dibarbet
Copy link
Member

closing this issue. there is a fix on the way (in the vscode dotnet runtime extension) and available workarounds

@igastru
Copy link

igastru commented Feb 4, 2025

I'm using Code OSS because it came with my distro and it was showing the same error message about: "Microsoft.CodeAnalysis.LanguageServer client: couldn't create a connection to server", but after I installed code-marketplace to enable vscode marketplace in Code OSS, I could install it, and it worked fine now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants