Skip to content

Index parsing errors are swallowed #14894

Closed
@jonhoo

Description

@jonhoo

Problem

When using a private registry, I recently ran into a case where a crate version that is in the index (confirmed by cURLing the registry directly) could not be resolved by Cargo, giving me an error like

error: no matching package named `something` found
location searched: `private-registry` index

I quickly suspected that the index entry was invalid somehow, but Cargo gave no errors or warnings, even with CARGO_LOG=trace. Furthermore, by tweaking http_remote.rs, I found that its query to the registry (with an if-none-match header) returned NotModified, indicating Cargo did have the etag of the latest index file, which did include the crate/version in question. By removing the etag header, I also confirmed that the contents Cargo got from its request contained the version in question.

Long story short, I discovered that Cargo ends up swallowing errors related to invalid index entries. The first time Cargo downloads an index file with an invalid entry, it hits

bail!(
"optional dependency `{dep}` is not included in any feature\n\
Make sure that `dep:{dep}` is included in one of features in the [features] table."
);

However, that gets turned into a tracing::info in

Err(e) => {
// This should only happen when there is an index
// entry from a future version of cargo that this
// version doesn't understand. Hopefully, those future
// versions of cargo correctly set INDEX_V_MAX and
// CURRENT_CACHE_VERSION, otherwise this will skip
// entries in the cache preventing those newer
// versions from reading them (that is, until the
// cache is rebuilt).
tracing::info!(
"failed to parse {:?} registry package: {}",
relative,
e
);
continue;
}

The user is likely to miss this since Cargo tracing logs are off by default, so they will just observe that Cargo claims the version doesn't exist.

Unfortunately, the issue doesn't stop there. If the user then tries to run with CARGO_LOG=trace, they will then see no errors or warnings from Cargo. In fact, the logs will make it seem like the invalid entry doesn't exist at all. There will be no trace of it anywhere. This is because when Cargo parses an index entry, it caches the parsed lines so that it doesn't have to parse them again

cache.versions.push((version.clone(), line));

let cache_bytes = cache.serialize(index_version.as_str());
// Once we have our `cache_bytes` which represents the `Summaries` we're
// about to return, write that back out to disk so future Cargo
// invocations can use it.
cache_manager.put(name, &cache_bytes);

However, when a line can't be parsed, it doesn't cache that line (note the continue where the error gets turned into tracing::info!). It also doesn't leave that line as "unparsed" somewhere. So on subsequent invocations, Cargo grabs the version list from the cache manager to avoid parsing lines again, doesn't observe the (invalid) index entry for the version in question so it doesn't warn, then downloads the file again because it doesn't find the requested version, that download yields and empty response because the etag does match the latest index file, and so Cargo gives up.

So, two requests:

  1. Cargo should be louder when it finds an invalid index entry.
  2. Cargo should repeat the warning about an invalid index entry even if it's in an up-to-date cached version of the index file.

Steps

  1. Inject a new version with an invalid index entry into a registry.
  2. Take a dependency on said version.
  3. Run cargo build.
  4. Observe that Cargo only says "version not found" with no further warnings.
  5. Re-run cargo build with CARGO_LOG=trace.
  6. Observe that there is no information in the logs about the invalid index entry which caused the version not to be found.

Possible Solution(s)

No response

Notes

In my case, the index entry parsing error I was eventually able to extract by disabling the if-none-match logic and then running with CARGO_LOG=trace was:

failed to parse "so/me/something" registry package: optional dependency `uom_0_35` is not included in any feature
Make sure that `dep:uom_0_35` is included in one of features in the [features] table.

I suspect, but am not quite sure, that this may be an erroneous index entry manipulation by the private registry provider we're using. Although it could also be that cargo publish allowed publishing something with a nonsensical optional dependency in the first place?

Version

cargo 1.85.0 (05f54fdc3 2024-12-03)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-dependency-resolutionArea: dependency resolution and the resolverC-bugCategory: bugS-triageStatus: This issue is waiting on initial triage.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions