Skip to content

Commit 1191301

Browse files
Guard against workspace folders without a URI on initialize
When a client sends `workspaceFolders` on `initialize` and one of the entries has no `uri`, the `OnInitialize` handler threw a `NullReferenceException` while resolving the initial working directory (`workspaceService.WorkspaceFolders.FirstOrDefault()?.Uri.GetFileSystemPath()`). The `?.` only guarded the folder being null, not its `Uri`, so calling `GetFileSystemPath()` on a null `Uri` blew up, the `initialize` response was never returned, and the server was unusable for that client. The same hazard exists in every other place we dereference `folder.Uri` (`WorkspacePaths`, `GetRelativePath`, `FindFileInWorkspace`). The issue (#2300) reports this as Linux-only with a repro that includes a valid `uri`; that exact payload doesn't actually throw (I reproduced both ways by driving a built PSES over stdio). The real trigger is a workspace folder lacking a URI, which is what the captured stack trace (`PsesLanguageServer.cs:line 150`) points at. - Add `WorkspaceService.AddWorkspaceFolders`, which owns the invariant that every folder in `WorkspaceFolders` has a non-null `Uri`. It skips null folders and folders without a URI (logging a warning) and treats a null collection as "no folders yet", falling back to the existing single-root and CWD behavior. - Call it from `OnInitialize` instead of the inline null-check plus `AddRange`. - Add a regression test covering null input and uriless/null folders. Fixes #2300. Drafted by Copilot (Claude Opus 4.8). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 6ad4f46 commit 1191301

3 files changed

Lines changed: 56 additions & 4 deletions

File tree

src/PowerShellEditorServices/Server/PsesLanguageServer.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,7 @@ public async Task StartAsync()
140140

141141
// Set the workspace path from the parameters.
142142
WorkspaceService workspaceService = languageServer.Services.GetService<WorkspaceService>();
143-
if (initializeParams.WorkspaceFolders is not null)
144-
{
145-
workspaceService.WorkspaceFolders.AddRange(initializeParams.WorkspaceFolders);
146-
}
143+
workspaceService.AddWorkspaceFolders(initializeParams.WorkspaceFolders);
147144

148145
// Parse initialization options.
149146
JObject initializationOptions = initializeParams.InitializationOptions as JObject;

src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,35 @@ public WorkspaceService(ILoggerFactory factory)
106106

107107
public IEnumerable<string> WorkspacePaths => WorkspaceFolders.Select(i => i.Uri.GetFileSystemPath());
108108

109+
/// <summary>
110+
/// Adds the given workspace folders, ignoring any that are null or lack a URI.
111+
/// </summary>
112+
/// <remarks>
113+
/// Some LSP clients send workspace folders without a URI on initialize. Adding such a
114+
/// folder would later throw a <see cref="NullReferenceException"/> when its URI is
115+
/// dereferenced (e.g. when resolving the initial working directory or a relative path),
116+
/// breaking the handshake. See
117+
/// https://github.com/PowerShell/PowerShellEditorServices/issues/2300.
118+
/// </remarks>
119+
public void AddWorkspaceFolders(IEnumerable<WorkspaceFolder> workspaceFolders)
120+
{
121+
if (workspaceFolders is null)
122+
{
123+
return;
124+
}
125+
126+
foreach (WorkspaceFolder workspaceFolder in workspaceFolders)
127+
{
128+
if (workspaceFolder?.Uri is null)
129+
{
130+
logger.LogWarning("Ignored workspace folder without a URI: " + workspaceFolder?.Name);
131+
continue;
132+
}
133+
134+
WorkspaceFolders.Add(workspaceFolder);
135+
}
136+
}
137+
109138
/// <summary>
110139
/// Gets an open file in the workspace. If the file isn't open but exists on the filesystem, load and return it.
111140
/// <para>IMPORTANT: Not all documents have a backing file e.g. untitled: scheme documents. Consider using

test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,32 @@ internal static WorkspaceService FixturesWorkspace()
8686
};
8787
}
8888

89+
// Regression test for https://github.com/PowerShell/PowerShellEditorServices/issues/2300:
90+
// a client that sends a workspace folder without a URI on initialize used to crash the
91+
// server with a NullReferenceException when the URI was later dereferenced.
92+
[Fact]
93+
public void AddWorkspaceFoldersIgnoresNullAndUrilessFolders()
94+
{
95+
WorkspaceService workspace = new(NullLoggerFactory.Instance);
96+
97+
// Null collection is a no-op rather than a throw.
98+
workspace.AddWorkspaceFolders(null);
99+
Assert.Empty(workspace.WorkspaceFolders);
100+
101+
workspace.AddWorkspaceFolders(new[]
102+
{
103+
new WorkspaceFolder { Uri = DocumentUri.FromFileSystemPath(s_workspacePath), Name = "valid" },
104+
new WorkspaceFolder { Name = "missing-uri" },
105+
null
106+
});
107+
108+
WorkspaceFolder folder = Assert.Single(workspace.WorkspaceFolders);
109+
Assert.Equal("valid", folder.Name);
110+
111+
// The downstream dereferences that previously threw now succeed.
112+
Assert.Equal(s_workspacePath, Assert.Single(workspace.WorkspacePaths));
113+
}
114+
89115
[Fact]
90116
public void HasDefaultForWorkspacePaths()
91117
{

0 commit comments

Comments
 (0)