Skip to content

fix(path): clarify error message when path dependency has wrong package#16927

Open
raushan728 wants to merge 2 commits intorust-lang:masterfrom
raushan728:fix-15296
Open

fix(path): clarify error message when path dependency has wrong package#16927
raushan728 wants to merge 2 commits intorust-lang:masterfrom
raushan728:fix-15296

Conversation

@raushan728
Copy link
Copy Markdown
Contributor

@raushan728 raushan728 commented Apr 22, 2026

View all comments

Fixes #15296

When a path dependency specifies a package name that doesn't match what's
found at that path, Cargo now shows helpful hints about what packages exist
nearby instead of the confusing "location searched" message.

@rustbot rustbot added A-dependency-resolution Area: dependency resolution and the resolver S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 22, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 22, 2026

r? @epage

rustbot has assigned @epage.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: @ehuss, @epage, @weihanglo
  • @ehuss, @epage, @weihanglo expanded to ehuss, epage, weihanglo
  • Random selection from ehuss, epage, weihanglo

Copy link
Copy Markdown
Contributor

@epage epage left a comment

Choose a reason for hiding this comment

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

Comment thread src/cargo/core/resolver/errors.rs Outdated
Comment thread src/cargo/core/resolver/errors.rs Outdated
Comment thread src/cargo/core/resolver/errors.rs Outdated
crate::sources::RecursivePathSource::new(&path, dep.source_id(), gctx);

if let Ok(packages) = src.read_packages() {
if !packages.is_empty() {
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.

This is pretty deeply nested. I wonder if there is a better path for this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This nesting has been refactored the logic is now extracted into a
standalone helper function activation_error_path_hints which returns
early with false for each guard condition, eliminating the deep nesting.
The call site in activation_error is now a single line.

Comment thread src/cargo/core/resolver/errors.rs Outdated
for pkg in &packages {
let p_name = pkg.name().as_str();
if p_name == dep.package_name().as_str() {
let _ = writeln!(
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.

Should we be consistent in where we write the hints?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

all hints now consistently written to hints buffer only

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.

That wasn't what I meant. You have a loop for searching for items. In some cases it directly creates the hint and in some it handles it afterwards.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed - the loop now only collects data (exact_match, found_dir,
names_found) and all hints are written after the loop ends.

Comment thread src/cargo/sources/path.rs Outdated
pub fn load(&self) -> CargoResult<()> {
let mut package = self.package.borrow_mut();
if package.is_none() {
if !self.path.join("Cargo.toml").exists() {
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.

Why was this added?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was an earlier attempt to handle missing Cargo.toml - reverted because it was too broad and broke existing tests. Now i use typed error detection in registry.rs

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.

From #16927 (comment)

Note for reviewers: The change in registry.rs is needed to handle case 2 and case 3 (where bar/ exists as a directory but has no Cargo.toml). Without it the IO error fires before the resolver reaches activation_error_path_hints. The suppression is guarded by three conditions: path source, typed ManifestError with NotFound, and has_nearby_manifests confirming subdirectory packages exist.

Comment thread src/cargo/core/resolver/errors.rs Outdated
if s.is_empty() {
s.push('.');
}
if !s.ends_with('/') {
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.

Why are we doing this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

use file_name() to show the actual directory name (e.g. foo/) instead of ./ when the path equals the parent root

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.

That isn't a complete sentence, making it harder to follow what you mean. I don't see how a file_name() (which?) even fits into this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

When path == parent_root, strip_prefix returns an empty string. In that case we fall back to path.file_name() to show the directory name (e.g. foo/) rather than an empty or confusing path

Comment thread src/cargo/core/resolver/errors.rs Outdated
Comment thread src/cargo/core/resolver/errors.rs Outdated
is_handled_custom_path_error = true;
let _ = writeln!(
&mut msg,
"no matching package named `{}` found at `{}`\nnote: required by {}",
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.

This adds a note on only this "required by",

Either we want this generally and a commit before this should add it or we should remove thisd

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.

Looks like this was resolved by dropping the note:?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, resolved by dropping the note: prefix.

Comment thread tests/testsuite/path.rs Outdated
@rustbot rustbot added the A-registries Area: registries label Apr 23, 2026
@raushan728
Copy link
Copy Markdown
Contributor Author

Note for reviewers: The change in registry.rs is needed to handle
case 2 and case 3 (where bar/ exists as a directory but has no
Cargo.toml). Without it the IO error fires before the resolver
reaches activation_error_path_hints. The suppression is guarded by
three conditions: path source, typed ManifestError with NotFound,
and has_nearby_manifests confirming subdirectory packages exist.

@raushan728 raushan728 requested a review from epage April 23, 2026 12:47
Comment on lines -395 to 401
} else {
let _ = writeln!(
&mut msg,
"no matching package named `{}` found",
dep.package_name()
);
}
Copy link
Copy Markdown
Contributor

@epage epage Apr 23, 2026

Choose a reason for hiding this comment

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

This change involves a refactor where more things are put into the else branch, If this is kept, I'd recommend splitting that out into its own commit.

View changes since the review

Copy link
Copy Markdown
Contributor Author

@raushan728 raushan728 Apr 24, 2026

Choose a reason for hiding this comment

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

the else branch restructuring is gone since we switched to the else if let Some(packages) = alt_paths(dep, gctx) pattern.

let mut location_searched_msg = registry.describe_source(dep.source_id());
if location_searched_msg.is_empty() {
location_searched_msg = format!("{}", dep.source_id());
if !is_handled_custom_path_error {
Copy link
Copy Markdown
Contributor

@epage epage Apr 23, 2026

Choose a reason for hiding this comment

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

We have two if !is_handled_custom_path_error in a row. Is there a reason these aren't merged and we just get rid of is_handled_custom_path_error?

View changes since the review

Copy link
Copy Markdown
Contributor Author

@raushan728 raushan728 Apr 24, 2026

Choose a reason for hiding this comment

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

The two consecutive blocks are merged into one. The flag itself is still needed - location searched must appear for !candidates.is_empty() too.

Comment thread src/cargo/core/resolver/errors.rs Outdated
Comment on lines +366 to +367
} else {
if activation_error_path_hints(dep, parent, gctx, resolver_ctx, &mut msg, &mut hints) {
Copy link
Copy Markdown
Contributor

@epage epage Apr 23, 2026

Choose a reason for hiding this comment

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

If you make a function just for getting the recursive path source packages, you can keep the existing else-if pattern by having an } else if let Some(path_packages) = alt_paths(...)? { which I think will make this a bit cleaner / easier to follow

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed - extracted alt_paths() function and using
} else if let Some(packages) = alt_paths(dep, gctx) { pattern.

Comment on lines +734 to +743
let res = source.query(dep, kind, f).await;
if let Err(e) = &res {
if dep.source_id().is_path()
&& crate::core::resolver::errors::is_manifest_not_found(e)
&& has_nearby_manifests(dep)
{
return Ok(());
}
}
res.with_context(|| format!("unable to update {}", source.source_id()))
Copy link
Copy Markdown
Contributor

@epage epage Apr 23, 2026

Choose a reason for hiding this comment

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

This has really ballooned in complexity, including the idea of "catching" specific errors.

View changes since the review

Copy link
Copy Markdown
Contributor Author

@raushan728 raushan728 Apr 24, 2026

Choose a reason for hiding this comment

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

The registry.rs change was needed because without it, the IO error fires before the resolver reaches the diagnostic code for cases 2/3.

Is there a cleaner way you'd suggest to intercept this early failure so we can still provide helpful hints for those cases? Happy to rework the approach if you have a better path in mind.

@raushan728 raushan728 force-pushed the fix-15296 branch 2 times, most recently from d4eb6f0 to d5f8d89 Compare April 24, 2026 06:09
…ge (rust-lang#15296)

When a path dependency points to a directory with a different package,
show which package exists there and where the correct one can be found.
@raushan728
Copy link
Copy Markdown
Contributor Author

raushan728 commented Apr 24, 2026

Regarding registry.rs - every alternative approach I tried but they broke existing tests. This is the cleanest I could find while keeping everything green.

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

Labels

A-dependency-resolution Area: dependency resolution and the resolver A-registries Area: registries S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve error message when wrong package found in a path dependency

3 participants