Skip to content

PyPI override markers make source-package locks stale; PyPI-only refresh drops source records #7215

Open

Description

@rwgk

Problem

This report, reproducer, analysis, and suggested patch are entirely
agent-generated by OpenAI Codex, model gpt-6.1-sol, at the reporter's
request. They have not yet received independent human review.

A lock freshly produced by pixi lock fails pixi lock --check when a local
Conda source package also appears in a wheel's dependency closure and a PyPI
dependency override excludes that requirement with a false marker.

Repeated ordinary pixi lock commands alternate between two states: the local
source reference is removed, then restored. This blocks locked installations
and CI freshness checks. The same refresh path can also remove an unrelated
local source package.

Reproduced on Linux with Pixi 0.73.0, 0.81.0, and current source HEAD
38ceaa89ac03649aaaefb919d1e501ca3d074d88.
On this HEAD, --check is read-only and exits 1; ordinary lock still
alternates. The released versions additionally rewrite during --check.
This is a source-record removal/addition problem, distinct from the ordering
problem in #7000 / #7011.

Reduced Reproducer

Both attachments have been uploaded and checked against the tested originals:

The fixture contains one Linux environment, one tiny local Conda source
package, and one deterministic 1 KB local wheel. It needs no CUDA packages,
GPU, compiler, or PyPI index; first use downloads Python and the build backend
from conda-forge. The ZIP also includes validation logs and an optional second,
unrelated source package.

The essential manifest is:

[workspace]
channels = ["conda-forge"]
platforms = ["linux-64"]
preview = ["pixi-build"]

[dependencies]
python = "3.12.*"
source-dependency = { path = "source-dependency" }

[pypi-dependencies]
wheel-consumer = { path = "wheels/wheel_consumer-0.1.0-py3-none-any.whl" }

[pypi-options.dependency-overrides]
source-dependency = { version = "*", env-markers = "python_version < '0'" }

The wheel declares Requires-Dist: source-dependency>=99. The exclusion should
make that PyPI requirement inactive; the independently declared Conda source
dependency must remain present.

Extract the ZIP into a dedicated directory:

unzip pixi-source-lock-oscillation-repro-head-38ceaa89.zip -d repro-source-lock-oscillation
cd repro-source-lock-oscillation
python3 reproduce.py /absolute/path/to/pixi --expect-oscillation

The driver creates the fixture wheel, locks, confirms the failed freshness
check, and verifies four consecutive locks alternate between exactly two
byte-identical states. Expected behavior is an immediately fresh lock with
the local source reference retained.

Two Contributing Defects And Suggested Fix

  1. Freshness traversal evaluates the original Requires-Dist marker and
    looks for its package before honoring the effective override marker.
    Traversal at the tested HEAD.
    The suggested patch applies UV's Overrides::apply before marker evaluation
    and lookup, preserving optional-extra semantics.

  2. A PyPI-only refresh obtains existing Conda records using a conversion that
    silently drops partial source records. The next check notices the missing
    direct Conda dependency and resolves it again.
    Refresh path,
    dropping conversion.
    The suggested patch re-resolves Conda metadata when a PyPI-only update
    would otherwise reuse partial source records.

These are independently reproducible: the marker fix alone makes routine
checks stable, but an explicit pixi update --no-install wheel-consumer still
drops both the related and unrelated source packages without the second fix.

Local Validation Of The Suggested Patch

Against the tested HEAD plus both changes:

  • Four strict checks exit 0 with identical lockfile bytes.
  • Selective PyPI updates retain both source packages and leave the lock fresh.
  • Actual pixi run --locked builds the local source and imports both packages.
  • The full core unit suite passes serially: 344 passed, 1 ignored. All 133
    satisfiability tests pass, including four new marker/extra cases.
  • Two new source-preservation regression cases pass: standalone environment
    and shared solve group; both fail before the preservation change.
  • Existing source, PyPI, and update suites pass (50, 16, and 7 tests).

The patch is offered as an agent-generated starting point for maintainer
review. No Pixi pull request or source branch has been published.

Activity

  1. rwgk commented on Oct 9, 2026

    @rwgk
    Author

    FYI — We tried to work around this under NVIDIA/cuda-python#3059 and NVIDIA/cuda-python#3062, but when that still wasn't sufficient, we decided to pause our Pixi lockfile freshness checks (NVIDIA/cuda-python#3065) until this issue is resolved and the fix is released.

  2. leofang commented on Oct 9, 2026

    @leofang
  3. Stanley5249 commented on Oct 14, 2026

    @Stanley5249

    I want to keep a transitive PyPI dependency from being installed
    (vllm -> xgrammar -> triton), so I added an override with a marker that never
    matches. This may be defect 1 from the issue body. My workspace has no conda
    source package, only two editable PyPI path dependencies, on pixi 0.81.0 and
    linux-64.

    [tool.pixi.pypi-options.dependency-overrides]
    triton = { version = "*", env-markers = "sys_platform == 'never'" }

    Steps:

    1. pixi lock prints "Lock-file was already up-to-date", and triton stays in
      the lock file.
    2. pixi update triton removes triton from the lock file.
    3. pixi lock --check --dry-run then prints "lock file not up-to-date with the
      workspace".
  4. jpascucci-nv commented on Oct 21, 2026

    @jpascucci-nv

    I found that this issue reproduces on 0.82.1.

    • pixi lock --check still exits 1 and proposes removing the local source package.
    • Four ordinary pixi lock runs still alternate between the same two lockfile states.
    • A selective PyPI update still drops both the related source record and an unrelated source record; the next check proposes restoring them.
  5. leofang commented on Oct 29, 2026

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

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