investigate: extension throws error during analysis — root cause findings#1
Conversation
There was a problem hiding this comment.
Pull request overview
Adds an investigation write-up documenting suspected root causes for Source Doc’s Explain commands failing, with proposed fixes and a prioritization matrix to guide follow-up code changes.
Changes:
- Adds a new investigation document summarizing 7 findings across
explanationProvider.ts,codeLensProvider.ts, andextension.ts - Includes suggested fixes and reproduction steps for the most impactful issues
- Provides a priority matrix to help sequence remediation work
| timeout), it propagates up uncaught to `runExplain`, which shows it in the | ||
| status bar, but **the LRU cache is not written** — meaning every retry hits | ||
| the model again instead of surfacing a friendlier retry prompt. |
There was a problem hiding this comment.
In section 1, the doc says a non-LanguageModelError propagates up uncaught to runExplain and is shown in the status bar. In the current implementation, runExplain catches all errors and calls vscode.window.showErrorMessage(...), not a status bar message. Please adjust this description to match the actual error handling path/UI.
| timeout), it propagates up uncaught to `runExplain`, which shows it in the | |
| status bar, but **the LRU cache is not written** — meaning every retry hits | |
| the model again instead of surfacing a friendlier retry prompt. | |
| timeout), `runExplain` catches it and surfaces the message via | |
| `vscode.window.showErrorMessage(...)`, but **the LRU cache is not written** — | |
| meaning every retry hits the model again instead of surfacing a friendlier, | |
| cached retry prompt. |
| ## 4. `codeLensProvider.ts` — `isNoiseLine` comment-detection misses `//` mid-regex | ||
| **File:** `src/codeLensProvider.ts`, `isNoiseLine` | ||
|
|
||
| ```ts | ||
| if (/^(\\/\\/|#|--|%%|;)/.test(trimmed)) { return true; } | ||
| ``` | ||
|
|
||
| ### Issue | ||
| The pattern correctly identifies single-line comments *at the start of a | ||
| trimmed line*. However, import/use-statement guard placed **after** this check | ||
| can still let through lines that begin with a comment prefix from some | ||
| languages not listed (e.g. Lua `--`, Haskell `--`). These are unlikely to be | ||
| active languages today but the `sourceDoc.languages` config is user-editable, | ||
| so arbitrary language IDs can be added. | ||
|
|
There was a problem hiding this comment.
Section 4’s explanation/examples don’t match the current isNoiseLine implementation: the regex already includes --, so the examples “Lua --, Haskell --” aren’t actually missed. Also the heading mentions “misses // mid-regex”, but the snippet shown is anchored to the start of the trimmed line and does match // prefixes. Please revise this finding to reflect a concrete, reproducible gap (or remove it).
| languageRegistrations.forEach(d => context.subscriptions.push(d)); | ||
| ``` | ||
| Or simply push the `languageRegistrations` array reference once so VS Code | ||
| drains it on deactivation. | ||
|
|
There was a problem hiding this comment.
Section 5 suggests “push the languageRegistrations array reference once so VS Code drains it on deactivation”. context.subscriptions only disposes items that implement Disposable; pushing an array won’t be disposed. If you want a single subscription, wrap the array in a Disposable (e.g., a disposer that iterates and disposes the current registrations) or use Disposable.from(...) when registering.
| When `lines` is empty (e.g. a file that consists entirely of comments or | ||
| blank lines), `items.length === 0`, which causes | ||
| `Array.from({ length: 0 }, worker)` — effectively a no-op. The progress | ||
| notification is shown and dismissed with `"0 / 0 done"` which is confusing. |
There was a problem hiding this comment.
Section 6 claims the progress UI shows/dismisses with "0 / 0 done" when there are no non-noise lines. In extension.ts, the progress title is Source Doc: explaining ${lines.length} lines… and the progress.report message is only set inside the per-line loop, so with 0 lines the user will more likely see “explaining 0 lines…” (briefly) rather than “0 / 0 done”. Please update the doc to describe the actual behavior you observe from this code path.
| notification is shown and dismissed with `"0 / 0 done"` which is confusing. | |
| notification is created with the title `Source Doc: explaining 0 lines…` and is dismissed without any per-line `"N / total done"` updates, which can look like a brief, confusing flicker. |
| If an empty file is opened (`lineCount === 0`), calling `document.lineAt(0)` | ||
| throws a `RangeError`. VS Code normally guarantees at least one line, but | ||
| virtual/untitled documents and some language servers surface edge cases. |
There was a problem hiding this comment.
Section 7 motivates the document.lineAt(0) guard using “virtual/untitled documents”, but this CodeLens provider is registered with { scheme: 'file' }, so it won’t be invoked for untitled/virtual documents. If you want to keep this item, please reframe it as a general defensive guard (or cite a concrete case where a file-scheme TextDocument can have lineCount === 0).
| If an empty file is opened (`lineCount === 0`), calling `document.lineAt(0)` | |
| throws a `RangeError`. VS Code normally guarantees at least one line, but | |
| virtual/untitled documents and some language servers surface edge cases. | |
| `provideCodeLenses` assumes that `document.lineCount > 0` and calls | |
| `document.lineAt(0)` unconditionally. VS Code currently guarantees at least | |
| one line for normal `file`-scheme documents, so this should not occur in | |
| typical operation, but it still relies on that invariant — if it is ever | |
| violated (for example by an extension host or language feature bug that | |
| surfaces a `file` document with `lineCount === 0`), a `RangeError` would be | |
| thrown. A small `lineCount` check makes the code resilient to such edge | |
| cases at essentially zero cost. |
|
@copilot open a new pull request to apply changes based on the comments in this thread |
Overview
This PR documents the investigation into why the Source Doc extension throws (or silently fails) when a user triggers any of the Explain commands (
Explain Line,Explain Block,Explain File).A full static-analysis pass was performed across every file in
src/. Seven distinct issues were identified, ranging from a high-severity silent-cache-poison bug to a trivial double-semicolon typo.Findings at a Glance
explanationProvider.tsLanguageModelErrorcodes surfaced to users with no actionable guidanceexplanationProvider.tscodeLensProvider.tsCODE_RE;;(linter warning)codeLensProvider.tsisNoiseLinenoise-filter gap for user-added language IDsextension.tslanguageRegistrationsdisposables leak onsourceDoc.languagesconfig changeextension.tsexplainFileshows confusing"0 / 0 done"on all-comment/blank filescodeLensProvider.tsdocument.lineAt(0)called withoutlineCount > 0guard — potentialRangeErrorKey Bug: Empty Response Cached (Issue #2)
When Copilot returns an empty body (content-filtered, quota exhausted, token cancelled mid-stream) the empty string
''is cached. The next identical request returns''from cache and the user sees no ghost text and no error — it looks like the feature is permanently broken for that code snippet.Proposed fix:
Key Bug: Disposable Leak (Issue #5)
Every time the user edits
sourceDoc.languages, newCodeLensProviderregistrations are pushed into the local array but never tracked by VS Code's disposal system. They accumulate until the window is closed.Reproduction Steps (Issues #1 & #2)
.ts/.pyfile.LanguageModelErrorcode.Files Changed
docs/analysis-error-investigation.md— full write-up with code snippets, suggested fixes, and a prioritised matrix for all seven issues.Next Steps
RangeError)LanguageModelErrorcodes (issue investigate: extension throws error during analysis — root cause findings #1)