Skip to content

TypeScript/vtsls language servers hard-ignore any directory named coverage, hiding legitimate source dirs from symbol tools #1523

@BinarySo1o

Description

@BinarySo1o

I have:

If you have encountered an actual issue:

  • If using language servers (not the JetBrains plugin),
    • I performed <uv invocation> serena project health-checksee note: our local install is already patched, so it no longer reproduces; diagnosis below used per-file CLI extraction + path-vs-content isolation instead
    • I indexed the project as described in the documentation
  • I added sufficient explanation of my setup
  • I explained how the issue arose and added instructions on how to reproduce it
  • If the issue happens on an open-source project, I have added the link — project is closed-source; the repro below is project-independent
  • I provided a meaningful title and description

Bug

get_symbols_overview, find_symbol, and find_referencing_symbols fail for every file under a source directory named coverage/ (e.g. src/routes/coverage/):

ValueError: Cannot extract symbols from file src/routes/coverage/<x>.ts. Active languages: ['typescript_vts']

The same file extracts correctly at any path whose segments don't include coverage. find_referencing_symbols on symbols defined under such a directory silently returns nothing, even when references obviously exist elsewhere.

Root cause

The TypeScript language servers hard-ignore any directory named coverage by bare name, in is_ignored_dirname:

  • src/solidlsp/language_servers/typescript_language_server.py
  • src/solidlsp/language_servers/vts_language_server.py
@override
def is_ignored_dirname(self, dirname: str) -> bool:
    return super().is_ignored_dirname(dirname) or dirname in [
        "node_modules", "dist", "build", "coverage",
    ]

The intent is to skip Jest/nyc coverage-report output. But the match is on bare dirname, so it also excludes legitimate source directories named coverage — an ordinary domain/module name (in our case a REST API resource: src/routes/coverage/).

This is the same class of over-broad hardcoded exclusion narrowed for dot-dirs in #1187 / #1203 ("only hardcode what's universal; rely on gitignore for generated dirs"), and discussed for PHP vendor in #731 / #743.

Why it's confusing to diagnose

The exclusion is applied at the language-server layer (is_ignored_dirname), independent of Serena's project-level ignore machinery. So:

  • git check-ignore src/routes/coverage/x.tsnot ignored
  • serena project is_ignored_path src/routes/coverage/x.ts → reports not ignored
  • serena project index still counts the files

…yet symbol extraction refuses the file. There is no config hook feeding is_ignored_dirname (confirmed in ls.py / settings.py); the only analogous per-language override is PHP's ls_specific_settings.intelephense.ignore_vendor (added in #743). A user cannot un-ignore the directory via .serena/project.yml.

Re-indexing does not help, and is a tempting dead end. serena project index is incremental and treats the files as already cached (the run completes at thousands/sec — effectively a no-op), so it looks like it "did nothing wrong." Even a forced re-extraction wouldn't matter: the exclusion happens at symbol-extraction time in the language server, not in the index cache. We initially chased a stale-cache theory and rebuilt the cache repeatedly before realizing the directory was never being extracted in the first place.

Reproduction

  1. In any TypeScript project, create a source directory named coverage with a .ts file containing a top-level symbol — src/coverage/thing.ts:
    export function thing() { return 1; }
    …and a sibling outside it, src/elsewhere.ts:
    export function elsewhere() { return 2; }
  2. Ensure coverage is NOT in .gitignore or ignored_paths (it isn't, for a source dir).
  3. get_symbols_overview on src/elsewhere.ts → returns elsewhere. ✅
  4. get_symbols_overview on src/coverage/thing.tsValueError: Cannot extract symbols .... ❌

Confirming it's the path segment, not the file (airtight isolation)

Copy the failing file to two un-indexed locations — one whose path contains a coverage/ segment, one neutral — and call get_symbols_overview on each. Identical bytes; only the path differs. The coverage/-path copy fails, the neutral copy succeeds ⇒ the exclusion is keyed on the directory name, not file content. grep -rn '"coverage"' src/solidlsp/language_servers/ then points straight at the two is_ignored_dirname lists.

Expected behavior

Symbol tools should work on any non-gitignored source directory, including one named coverage. A real coverage-report directory is generated output and is essentially always gitignored, so gitignore already excludes it — the hardcoded entry is redundant for its intended purpose and harmful for source dirs (the reasoning accepted in #1203).

Proposed fix

Remove "coverage" from the hardcoded list in both TS language servers and rely on gitignore for real report dirs (consistent with #1203). node_modules / dist / build are left untouched to keep this a single logical change; dist / build are arguably the same class but far less likely to collide with source-directory names, so I've left that decision to maintainers. Happy to open a PR (linked).

Workaround (until a fix lands)

  1. Remove the "coverage", entry from is_ignored_dirname in both typescript_language_server.py and vts_language_server.py in your installed solidlsp (site-packages). Wiped by uv tool upgrade serena-agent.
  2. Add the real report dir to ignored_paths, root-anchored so it doesn't re-match nested source dirs:
    ignored_paths:
      - /coverage
  3. Stop the MCP server, rm <project>/.serena/cache/typescript/*.pkl, re-run serena project index (now at real-extraction speed), reconnect.

Setup

  • Serena: 1.5.3
  • MCP client: Claude Code
  • OS: macOS
  • Language: TypeScript (vtsls / typescript_vts)
  • Project: closed-source; repro above is project-independent

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions