Skip to content

fix: [#1776] prevent phantom projects from dbt_packages/ during cold-start race#1884

Draft
ralphstodomingo wants to merge 1 commit intomasterfrom
fix/1776-dbt-packages-watcher-race
Draft

fix: [#1776] prevent phantom projects from dbt_packages/ during cold-start race#1884
ralphstodomingo wants to merge 1 commit intomasterfrom
fix/1776-dbt-packages-watcher-race

Conversation

@ralphstodomingo
Copy link
Copy Markdown
Contributor

@ralphstodomingo ralphstodomingo commented Apr 12, 2026

Summary

Fixes #1776 (extension stuck in "Parsing projects" state).

The DBTWorkspaceFolder file-system watcher registers phantom DBTCoreProjectIntegration instances for every dbt_project.yml that dbt deps writes into dbt_packages/ during the cold-start window, before the root project's refreshProjectConfig has had a chance to populate packagesInstallPath. Each phantom spawns a Python bridge, tries to initialize an adapter against a non-existent profile, and either hangs or fails silently, leaving the status bar permanently showing "Parsing projects" with multiple unknown_<uuid> entries in the Diagnostics dump. This PR adds a race-independent static path-segment gate to the watcher filter so package directories are rejected immediately regardless of registration state.

Root cause

In src/dbt_client/dbtWorkspaceFolder.ts, the constructor creates a FileSystemWatcher on **/${DBT_PROJECT_FILE} at the moment the workspace folder is registered, before discoverProjects() is even called. The onDidCreate handler filters out package paths via notInDBtPackages(uri, this.dbtProjects.map(p => p.getPackageInstallPath())).

On the pre-fix code path, getPackageInstallPath() returns undefined until the root project has completed its asynchronous refreshProjectConfigcreatePythonDbtProjectproject.init_project() chain. That chain round-trips through the Python bridge and typically takes 2-5 seconds even on Linux + DuckDB, longer on Windows + Snowflake (reporter environments). During that window, the dynamic check trivially returned true because the list was empty:

private notInDBtPackages(uri, packagesInstallPaths) {
    for (const packagesInstallPath of packagesInstallPaths) {
      if (packagesInstallPath) {
        if (uri.startsWith(packagesInstallPath)) {
          return false;
        }
      }
    }
    return true;  // ← falls through with empty list
}

Meanwhile, the extension itself auto-runs dbt deps --project-dir ... --profiles-dir ... --target dev as part of rebuildManifest — visible in any cold-start log as "Installing dbt dependencies before first manifest rebuild". On a fresh clone with a packages.yml and no pre-existing dbt_packages/, that call creates one dbt_project.yml per package, and the watcher's onDidCreate fires on each. Because the filter has no idea about the static dbt_packages/ convention, every one of those package files is accepted and registerDBTProject() is called on its parent directory. Each resulting DBTCoreProjectIntegration starts with projectName = "unknown_" + crypto.randomUUID() (see altimate-dbt-integration/src/dbtCoreIntegration.ts:194), spawns its own Python bridge, attempts to resolve a profile (there is none — it's a package dir, not a project), and wedges.

The status bar in src/statusbar/versionStatusBar.ts:70-76 shows "$(sync~spin) Parsing projects" (plural) exactly when event.projects.length > 1, and event.projects is filtered by inProgress=true. So the reporter's screenshot of the plural label is hard evidence that 2+ DBTProject instances were simultaneously in rebuildManifest. Under the race described above, that's exactly what happens: root project + N phantom package projects.

Commenter tphillip33's Diagnostics dump in #1776 shows the expected downstream symptoms:

Project Name=unknown_30cd7066-bacf-4304-8402-8021b603d0fa
Adapter Type=unknown
DBT is not initialized properly
Python bridge is connected
DBT Project File path=c:\Git\repos\phi_omop\icref_provider\dbt_project.yml
Target path not found
PackageInstall path not found
Manifest path not found
Catalog path not found

The unknown_<uuid> name, the Python bridge is connected line, and the entirely unpopulated target/packages/manifest/catalog paths match the state of a DBTCoreProjectIntegration whose refreshProjectConfig has not completed. Commenter dnabb independently observed the literal phenomenon: "from time to time the extension still seems to parse the dbt_project.yml files from extensions installed inside dbt_packages and dbt_internal_packages instead of the dbt_project.yml from the main file."

Fix

notInDBtPackages now delegates to a new exported free function isDBtProjectFileOutsidePackageDir that combines two checks:

  1. Static path-segment gate (new). Splits the path on both separators and rejects the URI if any segment equals dbt_packages, dbt_internal_packages, or site-packages. This matches the same directory-name list that discoverProjects() already passes to workspace.findFiles() as an exclude pattern, so both the async discovery path and the watcher path now reject the same set of package directories. The check is race-independent — it runs regardless of whether any project has finished refreshProjectConfig.

  2. Dynamic packages-install-path gate (preserved). Still iterates the registered projects' getPackageInstallPath() values to catch custom package directories configured via the packages-install-path key in dbt_project.yml, which aren't in the static list. This only kicks in after at least one project has finished initializing, but that's fine because custom-path users also get the race-independent static gate for the common name.

The static gate uses segment matching, not prefix matching, to avoid false positives on directories that merely start with dbt_packages_backup etc. Both Windows (\) and POSIX (/) separators are handled.

Extracting to a free function is purely for testability — it has no dependency on this, vscode, or anything injected through DI, so the unit tests don't need to mock the class.

Tests

16 new Jest tests in src/test/suite/dbtWorkspaceFolder.test.ts, including a characterization test that inlines the verbatim pre-fix filter and asserts it returned true for a package path during the race window:

isDBtProjectFileOutsidePackageDir
  static package-directory gate
    ✓ rejects POSIX paths inside dbt_packages/
    ✓ rejects POSIX paths inside dbt_internal_packages/
    ✓ rejects POSIX paths inside site-packages/
    ✓ rejects Windows paths inside dbt_packages\
    ✓ rejects mixed-separator paths (Windows + forward slashes)
    ✓ rejects deeply nested packages inside other packages
    ✓ accepts a normal root project path
    ✓ accepts a nested real project (monorepo layout)
    ✓ does not match a directory whose name merely starts with dbt_packages
  dynamic packages-install-path gate
    ✓ rejects paths under a registered project's custom packages-install-path
    ✓ accepts paths outside every registered project's packages-install-path
    ✓ tolerates undefined entries in packagesInstallPaths
  race-condition guarantee (the #1776 regression)
    ✓ pre-fix filter WOULD register a phantom package project during the race window
    ✓ pre-fix filter WOULD register a phantom on Windows during the race window
    ✓ post-fix filter rejects the same phantom path during the race window
    ✓ exposes all three package directory names on the exported constant

Full Jest suite: 271 passed, 1 pre-existing skipped.

Docker E2E replication

Reproduction and verification were done by building both origin/master 9a0ba3ad and this branch, deploying each into an isolated code-server container against the same fixture, and running the same file-creation sequence during the cold-start race window. The fixture and the spew harness are identical between the two runs — only the extension dist differs.

Fixture

/tmp/fx-1776: a copy of test-fixtures/jaffle-shop-duckdb/ with an empty dbt_packages/ directory and a packages.yml that references dbt-labs/dbt_utils@1.1.1. This simulates a fresh clone where dbt deps has not yet been run — the real-world cold-start condition reported in the issue.

Reproduction harness

While the extension is activating (via Playwright against http://localhost:$PORT/?folder=/home/coder/project), a background docker exec loop creates 12 fake package directories inside dbt_packages/, each with a valid dbt_project.yml and a noop macro, spaced 0.3 s apart. This is a direct substitute for what the extension's own auto-dbt deps run does — just with deterministic content so the log output can be correlated precisely by package name:

docker exec $CT bash -c 'for i in 1..12; do
  mkdir -p /home/coder/project/dbt_packages/phantom_pkg_$i
  cat > .../dbt_packages/phantom_pkg_$i/dbt_project.yml <<YAML
name: "phantom_pkg_$i"
version: "1.0.0"
config-version: 2
macro-paths: ["macros"]
YAML
  mkdir -p .../dbt_packages/phantom_pkg_$i/macros
  echo "{% macro noop_$i() %}{% endmacro %}" > .../macros/noop.sql
  sleep 0.3
done'

Result on origin/master 9a0ba3ad (pre-fix)

Container: dbtpu-1776m. Log file: exthost1/innoverio.vscode-dbt-power-user/Log - dbt.log.

DBTCoreProjectIntegration:Registering dbt core project entries:

02:01:28.677  ...at /home/coder/project
02:01:29.996  ...at /home/coder/project/dbt_packages/phantom_pkg_1
02:01:30.332  ...at /home/coder/project/dbt_packages/phantom_pkg_2
02:01:30.640  ...at /home/coder/project/dbt_packages/phantom_pkg_3
02:01:30.950  ...at /home/coder/project/dbt_packages/phantom_pkg_4
02:01:31.272  ...at /home/coder/project/dbt_packages/phantom_pkg_5

DbtProject:Created core dbt project unknown_<uuid> entries:

02:01:28.677  unknown_90ee7c5f-1506-4958-af8c-502d81444deb  /home/coder/project
02:01:29.996  unknown_dd11713b-6857-44e5-891f-37fb6e9e4ba1  .../dbt_packages/phantom_pkg_1
02:01:30.332  unknown_db33dfa6-ae3a-40de-8608-caec40ef802f  .../dbt_packages/phantom_pkg_2
02:01:30.640  unknown_16a8ec30-571a-4cc5-9417-59351628a41f  .../dbt_packages/phantom_pkg_3
02:01:30.951  unknown_c4f67128-5c63-4dbe-ba18-f673426fca34  .../dbt_packages/phantom_pkg_4
02:01:31.272  unknown_3dc64731-67bc-47c1-901a-9506235e0761  .../dbt_packages/phantom_pkg_5

rebuildManifestStatusChange inProgress=true events: 6 total, occurring within a ~1.6 s window (02:01:31.227 through 02:01:32.863). With event.projects.length > 1 for that entire duration, the status bar in versionStatusBar.ts:70-76 shows "$(sync~spin) Parsing projects" plural — the exact phrase from Vgrunert's screenshot.

Only 5 phantoms landed instead of 12 because the later files (phantom_pkg_6 onward) were created after the root project's refreshProjectConfig had begun populating packagesInstallPath, at which point the dynamic gate started catching them. This is consistent with the "from time to time" intermittency dnabb described: the race window has a variable edge that depends on exactly when dbt deps finishes writing each package file.

Result on this branch (post-fix)

Container: dbtpu-1776f. Same fixture. Same reproduction harness. Same 12 phantom packages created inside the same timing window.

DBTCoreProjectIntegration:Registering dbt core project entries:

02:06:14.631  ...at /home/coder/project

DbtProject:Created core dbt project unknown_<uuid> entries:

02:06:14.631  unknown_3ba0ab16-17db-4a6a-a0aa-c759657a2393  /home/coder/project

rebuildManifestStatusChange inProgress=true events: exactly 1. The status bar sees event.projects.length === 1 and shows "Parsing jaffle_shop" singular (which then clears normally).

Zero phantom registrations. Zero phantom unknown_<uuid> projects. The 12 phantom files still exist on disk, the watcher still fires onDidCreate for each of them, but the new static path-segment gate rejects them all before they reach registerDBTProject.

Extra: happy-path regression check on the fix branch

Same container, packages.yml removed, dbt_packages/ cleaned up, extension restarted. Root project with no packages at all:

02:09:18.274  Registering dbt core project at /home/coder/project
02:09:18.274  DbtProject:Created core dbt project unknown_13ff567e-...
02:09:19.759  Project config refreshed successfully for "jaffle_shop"
02:09:20.026  NodeParser:Parsing nodes for "jaffle_shop"
02:09:20.029  rebuildManifestStatusChange inProgress=false

The transient unknown_<uuid> placeholder correctly transitions to the real project name jaffle_shop, NodeParser runs, rebuild completes, status bar clears. The fix does not regress the happy path.

Extra-extra: phantom spew on a clean root on the fix branch

Same container, clean root reloaded, then the same 12-phantom spew replayed. Result:

02:09:51.686  Registering dbt core project at /home/coder/project
02:09:51.686  DbtProject:Created core dbt project unknown_f9d5e555-...
02:09:53.061  Project config refreshed successfully for "jaffle_shop"
02:09:53.064  rebuildManifestStatusChange inProgress=true
02:09:53.284  rebuildManifestStatusChange inProgress=false

Root project parses normally. Zero phantoms despite 12 phantom files created during the race window. One clean inProgress transition. This test is the strictest version because it also proves the root project's refresh path still works while the static gate is filtering the phantom spew in parallel.

Test plan

  • Jest dbtWorkspaceFolder.test.ts — 16 tests including pre-fix characterization + post-fix verification (npx jest --testPathPattern="dbtWorkspaceFolder.test")
  • Full Jest suite — 271 passed, 1 pre-existing skipped
  • Docker E2E reproduction on origin/master 9a0ba3ad: 5 phantoms observed, plural status bar confirmed
  • Docker E2E verification on this branch: 0 phantoms, singular status bar
  • Happy-path regression on this branch (no packages.yml): root parses cleanly, rebuildManifest completes
  • Phantom-spew regression on this branch (clean root): root parses cleanly, zero phantoms, one inProgress transition
  • Reviewer: optional manual check with a real Snowflake profile and a real packages.yml to confirm the cold-start symptoms are resolved end-to-end in the reporter environment

Related findings not in this PR

Two secondary issues were identified during the root-cause analysis and are not fixed here to keep the patch narrow. They are worth follow-up issues:

  1. Unbounded Python bridge calls in dbtCoreIntegration.ts:355-367. refreshProjectConfig awaits python.ex\project.init_project()`with no timeout. python-bridge's Bluebird promises expose a.timeout(ms) method (python-bridge/index.d.ts:54-58) that no caller uses. Any Python-side hang — slow adapter init, DNS timeout, SSO flow — wedges the whole project indefinitely and the try/catchindbtIntegrationAdapter.ts:285` can't fire because no error is thrown. Even with Extension 'stuck' in Parsing projects state #1776 fixed, a real root project whose adapter is genuinely slow will still look "stuck" to users.

  2. Silent error swallowing in dbtIntegrationAdapter.ts:285-329. refreshProjectConfig catches errors but only adds user-visible diagnostics for YAMLError and PythonException. Other error types (ERR_IPC_DISCONNECTED, IPC timeouts, network errors) are logged at debug level and the function returns silently, leaving the project stuck in the unknown_<uuid> placeholder state with no surfaced diagnostic. After this PR the phantom-project amplifier is gone, but a single genuine root failure still has poor visibility.

Both findings belong in altimate-dbt-integration, not this repo.

…d-start race

`DBTWorkspaceFolder.createConfigWatcher` fires `onDidCreate` for every
`dbt_project.yml` that appears inside the workspace, and before this fix the
only gate against registering packages as projects was a dynamic check
against the `packages-install-path` of already-registered projects. During
cold start the watcher is active immediately, but `getPackageInstallPath()`
is only populated once the root project's `refreshProjectConfig` has run
its Python bridge round-trip — typically 2-5 seconds, longer on slow
adapters. In that window, `notInDBtPackages` was called with an empty
`packagesInstallPaths` list and trivially returned `true`, so every file
that `dbt deps` wrote into `dbt_packages/` got registered as a phantom
`DBTCoreProjectIntegration` with `projectName = "unknown_" + uuid`.

Each phantom spawns its own Python bridge, tries to initialize an adapter
against a non-existent profile, and either hangs or fails silently. The
status bar's `event.projects.length > 1` branch then trips and shows
"Parsing projects" plural forever.

Fix adds a static path-segment gate to the watcher filter: any path
containing `dbt_packages`, `dbt_internal_packages`, or `site-packages` as
a segment is rejected regardless of the dynamic packages-install-path
list. The dynamic check remains for custom `packages-install-path`
overrides. The gate is extracted into a free function
`isDBtProjectFileOutsidePackageDir` so it can be unit-tested directly.

- 16 new Jest tests in `src/test/suite/dbtWorkspaceFolder.test.ts`,
  including a characterization of the pre-fix behavior (inlined copy of
  the old filter) and the post-fix behavior for the same inputs.
- Full Jest suite still green (271 passed, 1 pre-existing skipped).
- Docker E2E before/after verified against `origin/master` `9a0ba3ad` vs
  this branch with the same fixture and the same timed `dbt_project.yml`
  spew into `dbt_packages/`:
  - master: 5 phantom `unknown_<uuid>` projects registered under
    `dbt_packages/phantom_pkg_1..5`, 6 simultaneous `inProgress=true`
    rebuild events, status bar in "Parsing projects" plural.
  - fix: 0 phantom projects registered, 1 `inProgress=true` event,
    status bar correctly says "Parsing jaffle_shop".
- Happy-path regression check on the fix branch with a clean project
  (no `packages.yml`): root project parses normally, name transitions
  from `unknown_<uuid>` to `jaffle_shop`, NodeParser runs, rebuild
  completes.

Closes #1776.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 12, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 43b3355d-a2c5-43f3-832f-6f308e1878db

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1776-dbt-packages-watcher-race

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ralphstodomingo
Copy link
Copy Markdown
Contributor Author

ralphstodomingo commented Apr 12, 2026

Raw log evidence

Saved from the two containers (dbtpu-1776m on origin/master 9a0ba3ad, dbtpu-1776f on this branch). Same fixture, same 12-phantom spew harness, same Playwright navigation timing. Only difference: which dist/extension.js is mounted into /home/coder/extension-src/.

Pre-fix (origin/master 9a0ba3ad)

Filtered to the lines that prove the race:

2026-04-12 02:01:28.677 [debug] DBTCoreProjectIntegration:Registering dbt core project at /home/coder/project []
2026-04-12 02:01:28.677 [debug] DbtProject:Created core dbt project unknown_90ee7c5f-1506-4958-af8c-502d81444deb at file:///home/coder/project []
2026-04-12 02:01:29.996 [debug] DBTCoreProjectIntegration:Registering dbt core project at /home/coder/project/dbt_packages/phantom_pkg_1 []
2026-04-12 02:01:29.996 [debug] DbtProject:Created core dbt project unknown_dd11713b-6857-44e5-891f-37fb6e9e4ba1 at file:///home/coder/project/dbt_packages/phantom_pkg_1 []
2026-04-12 02:01:30.332 [debug] DBTCoreProjectIntegration:Registering dbt core project at /home/coder/project/dbt_packages/phantom_pkg_2 []
2026-04-12 02:01:30.332 [debug] DbtProject:Created core dbt project unknown_db33dfa6-ae3a-40de-8608-caec40ef802f at file:///home/coder/project/dbt_packages/phantom_pkg_2 []
2026-04-12 02:01:30.640 [debug] DBTCoreProjectIntegration:Registering dbt core project at /home/coder/project/dbt_packages/phantom_pkg_3 []
2026-04-12 02:01:30.640 [debug] DbtProject:Created core dbt project unknown_16a8ec30-571a-4cc5-9417-59351628a41f at file:///home/coder/project/dbt_packages/phantom_pkg_3 []
2026-04-12 02:01:30.950 [debug] DBTCoreProjectIntegration:Registering dbt core project at /home/coder/project/dbt_packages/phantom_pkg_4 []
2026-04-12 02:01:30.951 [debug] DbtProject:Created core dbt project unknown_c4f67128-5c63-4dbe-ba18-f673426fca34 at file:///home/coder/project/dbt_packages/phantom_pkg_4 []
2026-04-12 02:01:31.272 [debug] DBTCoreProjectIntegration:Registering dbt core project at /home/coder/project/dbt_packages/phantom_pkg_5 []
2026-04-12 02:01:31.272 [debug] DbtProject:Created core dbt project unknown_3dc64731-67bc-47c1-901a-9506235e0761 at file:///home/coder/project/dbt_packages/phantom_pkg_5 []
2026-04-12 02:01:31.227 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=true []
2026-04-12 02:01:31.316 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=true []
2026-04-12 02:01:31.729 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=true []
2026-04-12 02:01:32.133 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=true []
2026-04-12 02:01:32.569 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=true []
2026-04-12 02:01:32.863 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=true []
2026-04-12 02:01:33.946 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=false []
2026-04-12 02:01:34.266 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=false []
2026-04-12 02:01:34.475 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=false []
2026-04-12 02:01:34.485 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=false []
2026-04-12 02:01:34.704 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=false []

Count: 6 DBTCoreProjectIntegration:Registering entries (1 root + 5 phantoms), 6 simultaneous inProgress=true events, status bar shows "Parsing projects" plural because event.projects.length > 1.

Post-fix (this branch, packages.yml fixture with dbt-labs/dbt_utils)

Same container, same spew, same timing:

2026-04-12 02:06:14.631 [debug] DBTCoreProjectIntegration:Registering dbt core project at /home/coder/project []
2026-04-12 02:06:14.631 [debug] DbtProject:Created core dbt project unknown_3ba0ab16-17db-4a6a-a0aa-c759657a2393 at file:///home/coder/project []
2026-04-12 02:06:15.974 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=true []
2026-04-12 02:06:15.975 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=false []

Count: 1 DBTCoreProjectIntegration:Registering entry (just the root), 1 inProgress=true event, status bar shows "Parsing jaffle_shop" singular.

Zero phantom registrations despite the exact same 12 dbt_project.yml files being written into dbt_packages/ during the race window. The static path-segment gate rejected them all before they reached registerDBTProject().

Happy-path regression (no packages.yml, clean root)

Same container after removing packages.yml and resetting dbt_packages/:

2026-04-12 02:09:18.274 [debug] DBTCoreProjectIntegration:Registering dbt core project at /home/coder/project []
2026-04-12 02:09:18.274 [debug] DbtProject:Created core dbt project unknown_13ff567e-2280-4df0-bc26-1cf171858b83 at file:///home/coder/project []
2026-04-12 02:09:19.759 [debug] DBTProjectIntegrationAdapter:Project config refreshed successfully for "jaffle_shop" at /home/coder/project ["targetPaths","/home/coder/project/target","modelPaths",["/home/coder/project/models"],"seedPaths",["/home/coder/project/seeds"],"macroPaths",["/home/coder/project/macros"],"packagesInstallPath","/home/coder/project/dbt_packages","version",[1,11,8],"adapterType","duckdb"]
2026-04-12 02:09:19.762 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=true []
2026-04-12 02:09:20.026 [debug] NodeParser:Parsing nodes for "jaffle_shop" at /home/coder/project []
2026-04-12 02:09:20.029 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=false []

The transient unknown_13ff567e-... placeholder correctly transitions to the real name jaffle_shop via Project config refreshed successfully, NodeParser runs, rebuild completes. Fix does not regress the happy path.

Phantom spew against a clean, passing root (strictest test)

Same container, clean root, then 12-phantom spew replayed:

2026-04-12 02:09:51.686 [debug] DBTCoreProjectIntegration:Registering dbt core project at /home/coder/project []
2026-04-12 02:09:51.686 [debug] DbtProject:Created core dbt project unknown_f9d5e555-2818-41e1-a345-77688e24c22f at file:///home/coder/project []
2026-04-12 02:09:53.061 [debug] DBTProjectIntegrationAdapter:Project config refreshed successfully for "jaffle_shop" at /home/coder/project ["targetPaths","/home/coder/project/target","modelPaths",["/home/coder/project/models"],"seedPaths",["/home/coder/project/seeds"],"macroPaths",["/home/coder/project/macros"],"packagesInstallPath","/home/coder/project/dbt_packages","version",[1,11,8],"adapterType","duckdb"]
2026-04-12 02:09:53.064 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=true []
2026-04-12 02:09:53.284 [debug] DBTProject:Received rebuildManifestStatusChange event: inProgress=false []

Root registers, config refreshes to jaffle_shop, one inProgress transition, done. No phantoms despite 12 package files being written into dbt_packages/ during the race window.


How to re-run this reproduction

The fixture and harness are in test-fixtures/1776-stuck-parsing/ in my local test-fixtures store (not in this repo). For anyone reproducing:

  1. Copy the jaffle-shop-duckdb fixture to /tmp/fx-1776, remove target/ and logs/, drop a packages.yml with any real dbt package, and start with an empty dbt_packages/ directory.
  2. Deploy code-server with the repo mounted read-only at /home/coder/extension-src and /tmp/fx-1776 at /home/coder/project. The existing docker-setup/deploy-isolated.sh (see infra PR) handles this via ISSUE_NUM=1776m HOST_PORT=3040 DBT_PROJECT_PATH=/tmp/fx-1776.
  3. Open the browser to the code-server URL so the extension host starts activating.
  4. Within ~5 seconds, docker exec a shell loop that creates 12 dbt_packages/phantom_pkg_<N>/dbt_project.yml files with 0.3-second spacing. See the harness in the PR body's "Reproduction harness" section.
  5. Grep the extension log for DBTCoreProjectIntegration:Registering dbt core project and DbtProject:Created core dbt project unknown_. On master you'll see 5+ phantom entries; on this branch you'll see exactly 1 (the real root).

@github-actions
Copy link
Copy Markdown

Bundle Size Report

darwin-arm64: 70.1 MB
Category Size Compressed Files
Native: altimate-core 35.1 MB 14.0 MB 1
Media assets 29.6 MB 25.8 MB 89
Webview JS bundles 26.1 MB 8.3 MB 343
Native: zeromq 20.5 MB 8.2 MB 15
Webview images 15.3 MB 12.2 MB 18
Extension backend (JS) 2.7 MB 0.5 MB 2
Python packages 2.0 MB 0.5 MB 95
Native: other node_modules 1.0 MB 0.2 MB 139
Webview CSS 0.8 MB 0.1 MB 2
Webview other 0.5 MB 0.1 MB 5
Other 100 KB 24 KB 14
Total 133.6 MB 69.9 MB 723
linux-x64: 71.8 MB
Category Size Compressed Files
Native: altimate-core 41.8 MB 15.1 MB 1
Media assets 29.6 MB 25.8 MB 89
Webview JS bundles 26.1 MB 8.3 MB 343
Native: zeromq 21.9 MB 8.7 MB 16
Webview images 15.3 MB 12.2 MB 18
Extension backend (JS) 2.7 MB 0.5 MB 2
Python packages 2.0 MB 0.5 MB 95
Native: other node_modules 1.0 MB 0.2 MB 139
Webview CSS 0.8 MB 0.1 MB 2
Webview other 0.5 MB 0.1 MB 5
Other 100 KB 24 KB 14
Total 141.8 MB 71.6 MB 724
win32-x64: 72.7 MB
Category Size Compressed Files
Native: altimate-core 50.3 MB 16.2 MB 1
Media assets 29.6 MB 25.8 MB 89
Webview JS bundles 26.1 MB 8.3 MB 343
Native: zeromq 20.0 MB 8.1 MB 15
Webview images 15.3 MB 12.2 MB 18
Extension backend (JS) 2.7 MB 0.5 MB 2
Native: other node_modules 2.3 MB 0.7 MB 147
Python packages 2.0 MB 0.5 MB 95
Webview CSS 0.8 MB 0.1 MB 2
Webview other 0.5 MB 0.1 MB 5
Other 0.1 MB 24 KB 14
Total 149.6 MB 72.6 MB 731

@ralphstodomingo ralphstodomingo self-assigned this Apr 12, 2026
async discoverProjects() {
// Ignore dbt_packages and venv/site-packages/dbt project folders
const excludePattern =
"**/{dbt_packages,site-packages,dbt_internal_packages}";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isnt this supposed to take care of this?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extension 'stuck' in Parsing projects state

2 participants