Skip to content

Fix assertion failure when a package constrains itself#230

Open
dralley wants to merge 1 commit into
prefix-dev:mainfrom
dralley:constrains-itself
Open

Fix assertion failure when a package constrains itself#230
dralley wants to merge 1 commit into
prefix-dev:mainfrom
dralley:constrains-itself

Conversation

@dralley
Copy link
Copy Markdown
Contributor

@dralley dralley commented Jun 1, 2026

When a package both provides and conflicts with the same capability (example: in CentOS Stream, "centos-stream-release" provides "system-release" and also conflicts with "system-release"), the constrains clause would try to forbid the package from coexisting with itself. This caused WatchedLiterals::constrains to panic with "both literals cannot be false" because both watched literals referred to the same variable.

Skip self-referential constrains entries: a package that is already in the solution obviously cannot constrain itself out of it.

Assisted-By: Claude Opus 4.6 noreply@anthropic.com

@dralley
Copy link
Copy Markdown
Contributor Author

dralley commented Jun 1, 2026

centos-stream-release details: https://centos.pkgs.org/10-stream/centos-baseos-x86_64/centos-stream-release-10.0-21.el10.noarch.rpm.html

Provides: system-release = 10.0-21.el10
Conflicts: system-release

This is admittedly a strange thing to do, but here was the rationale: https://gitlab.com/redhat/centos-stream/rpms/centos-stream-release/-/commit/ce231c10b48cb4ddde860bfc9d6f79c76ba92e0a

I believe the semantics here are basically "if this package is installed, you must not install another provider, and if another provider is installed, you cannot install this package".

@dralley
Copy link
Copy Markdown
Contributor Author

dralley commented Jun 1, 2026

Let me know the ideal way to provide tests, unless you want to do it

Copy link
Copy Markdown
Contributor

@baszalmstra baszalmstra left a comment

Choose a reason for hiding this comment

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

The constraint introduces a tautology. I think a more technically correct approach would be to (not crash and) block the package from being installable using an assertion. This would be the same as when this is done through a transitive dependency (e.g. A depending on B, which constrains A). I don't think skipping the clause is the right approach. But maybe I misunderstand the use-case?

@dralley
Copy link
Copy Markdown
Contributor Author

dralley commented Jun 3, 2026

So, it's a package that is installed and can be updated (on my CentOS Stream dev container it's at release 21)

[root@473fed33ed83 src]# sudo dnf info centos-stream-release
Last metadata expiration check: 0:00:15 ago on Wed 03 Jun 2026 02:36:52 AM UTC.
Installed Packages
Name : centos-stream-release
Version : 10.0
Release : 21.el10
Architecture : noarch
Size : 38 k
Source : centos-stream-release-10.0-21.el10.src.rpm
Repository : @System
Summary : CentOS Stream release files
URL : https://centos.org
License : GPL-2.0-or-later
Description : CentOS Stream release files.

I haven't tracked down exactly what libsolv is doing to handle this scenario, but libsolv / DNF must be handling it somehow. Probably it's doing something similar.

@dralley dralley force-pushed the constrains-itself branch from b98c4a5 to ab34b25 Compare June 5, 2026 02:24
When a package both provides and conflicts with the same capability
(example: in CentOS Stream, "centos-stream-release" provides
"system-release" and also conflicts with "system-release"),
the constrains clause would try to forbid the package from coexisting
with itself.  This caused WatchedLiterals::constrains to panic with
"both literals cannot be false" because both watched literals referred
to the same variable.

Skip self-referential constrains entries: a package that is already
in the solution obviously cannot constrain itself out of it.

Assisted-By: Claude Opus 4.6 <noreply@anthropic.com>
@dralley dralley force-pushed the constrains-itself branch from ab34b25 to c16fc12 Compare June 5, 2026 02:37
@dralley
Copy link
Copy Markdown
Contributor Author

dralley commented Jun 5, 2026

Ah, so here's some libsolv documentation:

https://github.com/openSUSE/libsolv/blob/2f58c6f86edd978d6bdbd87dce9c85388e9b9dcc/doc/libsolv-pool.txt?plain=1#L207C1-L210C50

https://github.com/openSUSE/libsolv/blob/2f58c6f86edd978d6bdbd87dce9c85388e9b9dcc/doc/libsolv-bindings.txt?plain=1#L345-L348

https://github.com/openSUSE/libsolv/blob/2f58c6f86edd978d6bdbd87dce9c85388e9b9dcc/src/rules.c#L985-L993

So, libsolv does indeed do basically the same thing as this patch, but it's behind a solver configuration flag, because the desired behavior varies by ecosystem. Would you want to do something similar (or is there already a mechanism for solver config options)?

Unrelated: looks like the multiversion code is right there as well. And that might need a similar solution as well

@baszalmstra
Copy link
Copy Markdown
Contributor

Is that then something we should not just solve in the dependency provider itself? Simply dont return self-referential constraints?

We should still solve the crash of course, but I think we should backtrack in that case.

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.

2 participants