Skip to content

Registry errors are reported as "<port> does not exist", hiding the real failure #2094

Description

@bugale

Summary

VersionedPortfileProviderImpl::entry() discards the real error returned by RegistryImplementation::get_port_entry and replaces it with {package_name} does not exist. Any registry-level failure — network, git, filesystem, lock contention — is reported to the user as "this port doesn't exist", which is both wrong and undiagnosable.

Where

{
VersionedPortfileProviderImpl(const RegistrySet& rset) : m_registry_set(rset) { }
VersionedPortfileProviderImpl(const VersionedPortfileProviderImpl&) = delete;
VersionedPortfileProviderImpl& operator=(const VersionedPortfileProviderImpl&) = delete;
const ExpectedL<std::unique_ptr<RegistryEntry>>& entry(StringView name) const
{
auto entry_it = m_entry_cache.find(name);
if (entry_it == m_entry_cache.end())
{
if (auto reg = m_registry_set.registry_for_port(name))
{
if (auto entry = reg->get_port_entry(name))
{
entry_it = m_entry_cache.emplace(name.to_string(), std::move(entry)).first;
}
else
{
entry_it = m_entry_cache
.emplace(name.to_string(),
msg::format(msgPortDoesNotExist, msg::package_name = name))
.first;
}
}
else
{
entry_it = m_entry_cache
.emplace(name.to_string(),
msg::format_error(msgNoRegistryForPort, msg::package_name = name))

if (auto reg = m_registry_set.registry_for_port(name))
{
    if (auto entry = reg->get_port_entry(name))
    {
        entry_it = m_entry_cache.emplace(name.to_string(), std::move(entry)).first;
    }
    else
    {
        entry_it = m_entry_cache
                       .emplace(name.to_string(),
                                msg::format(msgPortDoesNotExist, msg::package_name = name))   // <-- error dropped
                       .first;
    }
}

get_port_entry returns ExpectedL<std::unique_ptr<RegistryEntry>> and has three outcomes:

  1. error — something went wrong talking to the registry
  2. value, null pointer — the registry genuinely has no such port
  3. value, non-null — found

Case 2 is already handled further down, in load_control_file:

if (!ent->get())
{
    return msg::format_error(msgPortDoesNotExist, msg::package_name = version_spec.port_name);
}

So the else branch above is reached only in case 1, and it throws the diagnostic away. It also uses msg::format rather than msg::format_error, so the resulting line isn't even prefixed with error:.

Why it matters

This masked #2066 completely for us. A concurrent git init race in the registries cache (see #2066 / #2093) made get_port_entry fail, and all we ever saw was:

Fetching registry information from https://github.com/microsoft/vcpkg (HEAD)...
apr does not exist
apr-util does not exist
capstone does not exist
crc32c does not exist
expat does not exist
...

No error: prefix, no git output, no exit code, nothing. Every port from the default registry is listed as non-existent, which points suspicion at the manifest or the baseline rather than at the registry fetch that actually failed. It took a GIT_TRACE2_EVENT capture of a live failure to find the real message, which was:

could not lock config file .../registries/git/.git/config: File exists
could not set 'core.repositoryformatversion' to '0'

Note that #2066 hit the same underlying failure but through get_baseline_version, which propagates the error properly — so that reporter saw the actual git error and could describe the problem accurately. The difference is entirely down to this one else branch.

Suggested fix

Propagate the error instead of inventing one:

else
{
    entry_it = m_entry_cache.emplace(name.to_string(), std::move(entry).error()).first;
}

ExpectedT::error() && already exists, and the cache value type (ExpectedL<std::unique_ptr<RegistryEntry>>) is constructible from LocalizedString, so this is a drop-in replacement. Case 2 keeps producing msgPortDoesNotExist via load_control_file as before.

I've opened a PR with this change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions