Skip to content

[ty] Invalidate dependency metadata when environments change - #29126

Merged
zsol merged 2 commits into
mainfrom
zsol/ty-4604-environment-cache
Oct 7, 2026
Merged

zsol merged 2 commits into
mainfrom
zsol/ty-4604-environment-cache

Conversation

@zsol

@zsol zsol commented Oct 6, 2026

Copy link
Copy Markdown
Member

Project::dependency_metadata resolves Python environments inside a Salsa query without tracking those inputs. This can leave invalid-environment, missing-environment, and environment-mismatch diagnostics stale after an environment changes.

Store the selected environment's resolution result in ProgramSettings and track uv's environment directory through Salsa. Resolve uv's canonical path whenever the query runs so it observes symlink changes even when uv metadata is unchanged.

After a failed selection, retain the last usable environment for watching and recovery, and preserve the checking program's search paths. Dependency metadata reports the current environment error.

Fixes astral-sh/ty#4604.

Recompute dependency metadata when the resolved Python environment or
uv's tracked environment directory changes. Resolve symlink targets on
each query execution to avoid caching an outdated canonical path.

Keep failed environment selections in a Result while retaining the last
usable environment for watching and recovery.

Fixes astral-sh/ty#4604
@zsol zsol added bug An issue describing something that isn't working, or a PR that fixes a bug ty The ty type checker labels Oct 6, 2026
@zsol
zsol deployed to automations October 6, 2026 09:38 — with GitHub Actions Active
@astral-sh-bot

astral-sh-bot Bot commented Oct 6, 2026

Copy link
Copy Markdown

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 98.24%. The percentage of expected errors that received a diagnostic held steady at 98.24%. The number of fully passing files held steady at 134/146.

@astral-sh-bot

astral-sh-bot Bot commented Oct 6, 2026

Copy link
Copy Markdown

Memory usage report

Memory usage unchanged ✅

@astral-sh-bot

astral-sh-bot Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

ecosystem-analyzer results

No diagnostic changes detected ✅

Full report with detailed diff (timing results)

@astral-sh-bot

astral-sh-bot Bot commented Oct 6, 2026

Copy link
Copy Markdown

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@zsol
zsol marked this pull request as ready for review October 6, 2026 10:27
@zsol
zsol requested review from a team as code owners October 6, 2026 10:27
@astral-sh-bot
astral-sh-bot Bot requested a review from carljm October 6, 2026 10:27

@carljm carljm left a comment

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.

Thank you!

.map_err(|error| DependencyMetadataError::InvalidEnvironment {
path: environment.to_path_buf(),
message: error.to_string().into(),
})?;

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.

If .venv is a symlink to a directory named environment, deleting and recreating the environment directory can leave dependency metadata permanently stuck at InvalidEnvironment. Deleting and re-creating environment may not produce events for the unchanged .venv symlink; we need to be able to recover from the directory events alone, even when uv metadata hasn't changed.

After deletion, canonicalizing .venv fails and returns here before the query reads either the tracked status of environment or program_settings. Salsa drops those dependencies. Recreating environment then does not invalidate the cached error, even when program settings are refreshed successfully. The warning persists and dependency checks stay disabled.

On main, this sequence keeps returning cached success throughout deletion and recreation: it never notices the missing directory in the first place. An initially cached InvalidEnvironment could already remain stale, too. What is new here is that deleting a previously working environment now invalidates success, but recreating it leaves us stuck with the error.

I reproduced this by appending the following to dependency_metadata_tracks_environment_symlink, after its final deletion assertion:

std::fs::create_dir_all(environment.join("lib/python3.13/site-packages"))?;
std::fs::write(
    environment.join("pyvenv.cfg"),
    "home = /missing\nversion = 3.13.0\ninclude-system-site-packages = false",
)?;
File::sync_path(&mut db, &environment);
assert_matches!(project.dependency_metadata(&db), Ok(Some(_)));

The final assertion still gets InvalidEnvironment.

I think the safest fix is probably to call report_untracked_read() on the canonicalization error path?

.map_err(|error| {
    db.report_untracked_read();
    DependencyMetadataError::InvalidEnvironment {
        path: environment.to_path_buf(),
        message: error.to_string().into(),
    }
})?;

This makes Salsa retry the failed query in the next database revision, regardless of whether any dependencies have changed. Just preserving the dependency on program_settings isn't good enough, since then we only recover if the settings actually change.

Successful queries would still keep their normal selective invalidation; we would just keep retrying this canonicalization on every revision (even unrelated revisions) as long as the error persists. But this seems like probably the right behavior, since with a broken environment probably everything else is broken, too.

I verified this fixes direct directory synchronization, normal apply_changes delete/create events, and the case where ty selects a different environment from uv and its program settings stay unchanged. All 80 ty_project unit tests, including those three recovery cases, pass with this change.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fantastic catch! I think the suggested solution introduces a few edge cases, but none that I can see occuring during normal development, for example:

In a setup where uv's environment is a double symlink: .venv -> bridge/env -> actual_env, and ty's is directly actual_env, then the following sequence of events:

  1. check a python file that produces a missing-direct-dependency diagnostic for a dependency inside environment
  2. make bridge inaccessible by chmoding it -> this causes canonicalizing of uv's path to fail, but ty can still resolve the dependency through actual_env; however, missing-direct-dependency is turned off because the canonicalization failure
  3. restore bridge permissions; with the proposed solution, we'll retry and obtain dependency metadata even though .venv hasn't changed, so we'll use the cached import check which produced no diagnostic in step 2
  4. create an empty ty.toml and rediscover the project -> this causes a configuration change but the dependency metadata would not change, leading to still no diagnostics.

I think all of these edge cases are too contrived to warrant the complexity of solving them, so I'll just go with your suggestion which definitely improves things in (comparatively) normal cases.

@zsol
zsol deployed to automations October 7, 2026 13:41 — with GitHub Actions Active
@zsol
zsol force-pushed the zsol/ty-4604-environment-cache branch from bf133ff to c447a6e Compare October 7, 2026 14:19
@zsol
zsol enabled auto-merge (squash) October 7, 2026 14:20
@zsol
zsol deployed to automations October 7, 2026 14:20 — with GitHub Actions Active
@zsol
zsol merged commit e0da489 into main Oct 7, 2026
73 checks passed
@zsol
zsol deleted the zsol/ty-4604-environment-cache branch October 7, 2026 14:27

This branch was successfully deployed

1 active deployment
automations — c447a6e9 Deployed Oct 7, 2026 by zsol via security-review / security review #90992
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug An issue describing something that isn't working, or a PR that fixes a bug ty The ty type checker

Projects

None yet

Development

Successfully merging this pull request may close these issues.

project.dependency_metadata calls system APIs

2 participants