You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
language-server: external templates permanently lose language service in solution-style (Nx) workspaces — getDefaultProjectForScriptInfo lacks the #2165 fallback #69768
In workspaces whose app tsconfig.json is solution-style (references only — the default layout Nx generates), external templates (.component.html) permanently lose all language service features (go-to-definition, hover, diagnostics) once their association to a configured project is dropped, e.g. after the corresponding component .ts file is closed or after project-graph churn. Every subsequent request for the template fails with:
Err ... No config file for /path/to/foo.component.html
and returns null — silently, from the client's perspective — until the user manually opens the component .ts file again.
Root cause
A solution-style tsconfig.json contains only references, so ts.server.ProjectService.openClientFile() can never map an .html file to a configured project — no referenced tsconfig lists html files. PR #2165 addressed this in Session.onDidOpenTextDocument with a best-effort fallback: briefly open the sibling .ts file (which is included in a referenced project), forcing the right project to load, then re-open the template:
However, Session.getDefaultProjectForScriptInfo — the request-time recovery path used by getLSAndScriptInfo (hover/definitions/etc.) and onDidChangeTextDocument whenever a script info has no configured project — did not receive the same fallback. It still performs a bare lookup and dead-ends:
if(!project||project.projectKind!==ts.server.ProjectKind.Configured){const{configFileName}=this.projectService.openClientFile(scriptInfo.fileName);if(!configFileName){// Failed to find a config file. There is nothing we could do.this.error(`No config file for ${scriptInfo.fileName}`);returnnull;}
...
So whenever a template's project attachment is torn down after open (tab lifecycle, project updates), the server can never recover it. The template stays dead until the user opens the sibling .ts themselves, re-triggering the didOpen-time fallback indirectly.
This is editor-agnostic: reproduced both in Zed (where an aggressive buffer lifecycle makes template features unusable in Nx workspaces — see nathansbradshaw/zed-angular#89 for user reports) and in VS Code itself with the Angular extension v22.0.1 (server 22.x — see VS Code reproduction below). It is likely also a contributing factor in "No config file" reports that persist post-#2165 (e.g. #65514 shows the same terminal error string for a different trigger).
Reproduction
Against a large Nx monorepo (Angular 19 app, ~30 projects, solution-style apps/*/tsconfig.json), driving @angular/language-server 21.1.5 over raw LSP stdio:
initialize with the workspace root, didOpen a .component.html (languageId html) → feat(router): add routing to async components #2165's fallback loads the project, textDocument/definition on a component selector in the template resolves correctly (~9s cold / ~1ms warm). ✅
didOpen the sibling .component.ts, then didClose it, plus further open/close churn of other project files → the template's script info loses its configured project.
Any subsequent textDocument/definition/hover on the still-open template → server logs No config file for ...component.html and responds null, indefinitely (verified over minutes; 40+ consecutive failures). ❌
With --logFile/verbose tsserver logging, step 3 shows getDefaultProjectForScriptInfo's openClientFile returning no configFileName on every request (expected for a solution-style config), with no recovery attempt.
VS Code reproduction (Angular extension v22.0.1)
Same workspace, no custom LSP client involved:
Close all editors (cmd+K W).
Open an external template (*.component.html) directly — no TypeScript file open.
Restart VS Code so the session restores with only the template open.
Go to Definition on a component selector in the template does nothing, indefinitely.
Angular Language Service output panel:
[Error - 8:50:36 PM] No config file for /path/to/workspace/apps/my-app/src/app/components/foo/foo.component.html
[Error - 8:51:57 PM] No config file for /path/to/workspace/apps/my-app/src/app/components/foo/foo.component.html
Notably this is a template-opened-first session — the scenario #2149/#2165 was meant to cover — so the open-time best-effort is not sufficient on its own, and there is no recovery at request time.
Proposed fix
Mirror the #2165 fallback inside getDefaultProjectForScriptInfo: when configFileName comes back undefined and the file is an external template, briefly open/close the sibling .ts (guarded by projectService.openFiles, same as #2165) and retry; if the config lookup still fails but the sibling's script info has a configured project, attach the template's script info to that project directly.
We have been running exactly this patch (applied to the compiled 21.1.5 bundle) in daily use on the Nx monorepo above: template features now self-heal on the next request after any detach, with an A/B experiment confirming stock code fails (40+ No config file errors in 30s of normal use) and patched code recovers every time.
Happy to submit a PR with this change.
Please provide the environment you discovered this bug in
@angular/language-server: 21.1.5 (bug also present in current main source of session.ts)
Clients reproduced: VS Code (Angular extension v22.0.1, server 22.x), Zed 1.10.3 (community Angular extension, server 21.1.5), and a raw LSP stdio client (no editor involved)
Which @angular/* package(s) are the source of the bug?
language-server (vscode-ng-language-service/server)
Is this a regression?
Partially. The class of bug was reported in angular/vscode-ng-language-service#2149 and fixed by angular/vscode-ng-language-service#2165, but that fix only covers one of the two code paths that need it.
Description
In workspaces whose app
tsconfig.jsonis solution-style (references only — the default layout Nx generates), external templates (.component.html) permanently lose all language service features (go-to-definition, hover, diagnostics) once their association to a configured project is dropped, e.g. after the corresponding component.tsfile is closed or after project-graph churn. Every subsequent request for the template fails with:and returns
null— silently, from the client's perspective — until the user manually opens the component.tsfile again.Root cause
A solution-style
tsconfig.jsoncontains onlyreferences, sots.server.ProjectService.openClientFile()can never map an.htmlfile to a configured project — no referenced tsconfig lists html files. PR #2165 addressed this inSession.onDidOpenTextDocumentwith a best-effort fallback: briefly open the sibling.tsfile (which is included in a referenced project), forcing the right project to load, then re-open the template:https://github.com/angular/angular/blob/main/vscode-ng-language-service/server/src/session.ts (in
onDidOpenTextDocument):However,
Session.getDefaultProjectForScriptInfo— the request-time recovery path used bygetLSAndScriptInfo(hover/definitions/etc.) andonDidChangeTextDocumentwhenever a script info has no configured project — did not receive the same fallback. It still performs a bare lookup and dead-ends:So whenever a template's project attachment is torn down after open (tab lifecycle, project updates), the server can never recover it. The template stays dead until the user opens the sibling
.tsthemselves, re-triggering the didOpen-time fallback indirectly.This is editor-agnostic: reproduced both in Zed (where an aggressive buffer lifecycle makes template features unusable in Nx workspaces — see nathansbradshaw/zed-angular#89 for user reports) and in VS Code itself with the Angular extension v22.0.1 (server 22.x — see VS Code reproduction below). It is likely also a contributing factor in "No config file" reports that persist post-#2165 (e.g. #65514 shows the same terminal error string for a different trigger).
Reproduction
Against a large Nx monorepo (Angular 19 app, ~30 projects, solution-style
apps/*/tsconfig.json), driving@angular/language-server21.1.5 over raw LSP stdio:initializewith the workspace root,didOpena.component.html(languageIdhtml) → feat(router): add routing to async components #2165's fallback loads the project,textDocument/definitionon a component selector in the template resolves correctly (~9s cold / ~1ms warm). ✅didOpenthe sibling.component.ts, thendidCloseit, plus further open/close churn of other project files → the template's script info loses its configured project.textDocument/definition/hover on the still-open template → server logsNo config file for ...component.htmland respondsnull, indefinitely (verified over minutes; 40+ consecutive failures). ❌With
--logFile/verbose tsserver logging, step 3 showsgetDefaultProjectForScriptInfo'sopenClientFilereturning noconfigFileNameon every request (expected for a solution-style config), with no recovery attempt.VS Code reproduction (Angular extension v22.0.1)
Same workspace, no custom LSP client involved:
cmd+K W).*.component.html) directly — no TypeScript file open.Angular Language Service output panel:
Notably this is a template-opened-first session — the scenario #2149/#2165 was meant to cover — so the open-time best-effort is not sufficient on its own, and there is no recovery at request time.
Proposed fix
Mirror the #2165 fallback inside
getDefaultProjectForScriptInfo: whenconfigFileNamecomes backundefinedand the file is an external template, briefly open/close the sibling.ts(guarded byprojectService.openFiles, same as #2165) and retry; if the config lookup still fails but the sibling's script info has a configured project, attach the template's script info to that project directly.We have been running exactly this patch (applied to the compiled 21.1.5 bundle) in daily use on the Nx monorepo above: template features now self-heal on the next request after any detach, with an A/B experiment confirming stock code fails (40+
No config fileerrors in 30s of normal use) and patched code recovers every time.Happy to submit a PR with this change.
Please provide the environment you discovered this bug in
mainsource ofsession.ts)referencestotsconfig.app.json/tsconfig.spec.json/tsconfig.editor.json)