Yanking and re-releasing a broken spatial wheel

This page answers one question: you have published a release whose Linux wheel fails to import, and the file cannot be replaced — so what exactly do you yank, what do you publish instead, and what do you tell people while they are still hitting it? It sits inside the Publishing and Distributing Spatial Wheels section of the Modern Python Build Tooling & Wheel Configuration reference, and gives you the decision, the sequence and the follow-up that stops it recurring.

What yanking does and does not do to a published release A yanked release remains installable for anyone who pins the exact version, so existing lock files keep resolving. It is removed from ordinary resolution, so a new install picks the previous good version instead. It cannot be deleted and its filenames can never be reused. And it does nothing for users who have already installed it, who need a new version to upgrade to. still installable when pinned an existing lock file naming the exact version keeps resolving so a yank does not break a reproducible build overnight removed from ordinary resolution a fresh install falls back to the previous good version which is the entire point of the mechanism cannot be deleted or replaced the filenames are permanently taken — a fix needs a new version number and re-uploading the same version is refused does nothing for existing installs they need a newer version to upgrade to, which is the real fix

Context & Root Cause

A published file on an index is immutable. That is a deliberate property — it is what makes a lock file meaningful — and it means the only remedies for a broken release are to hide it from resolution and to publish a fixed one. Deleting is not available, and re-uploading the same version is refused.

Spatial packages hit this more than most, for a reason that is structural rather than careless: the failure is frequently platform-specific. A wheel that imports fine on the maintainer’s Linux machine fails on macOS because a bundled library declared a higher deployment target, or on Alpine because the musllinux cell silently produced an unrepaired artifact. The release looks correct from every angle the maintainer can see, and the breakage arrives as an issue from a platform they do not run.

Solution / Fix

This targets a public index supporting yanking, twine 5.x, and the release pipeline described in publishing and distributing spatial wheels.

1. Establish the blast radius before touching anything

# Which platforms are actually broken?
for img in python:3.12-slim python:3.12-alpine; do
  docker run --rm $img sh -c "pip install -q geo-core==2.4.1 && python -c 'import geo_core'" \
    && echo "$img ok" || echo "$img BROKEN"
done

2. Yank the version, not the project

# Yanking is per release; a reason string is shown to users who pin it
twine yank geo-core 2.4.1 --reason "macOS wheels fail to import; fixed in 2.4.2"

If the index’s client does not support yanking directly, the same action is available through the project’s web interface — and it is per version, so a release with one broken platform is yanked whole.

3. Publish the fix as a new patch version

git tag v2.4.2 && git push --tags     # the release job builds and publishes

4. Say what happened where people will see it

## 2.4.2

Fixes the macOS wheels published in 2.4.1, which failed to import with
`incompatible architecture` because a bundled library targeted macOS 14.

2.4.1 has been yanked. If you pinned it, upgrade to 2.4.2; if you installed
it without a pin, a fresh install already resolves to 2.4.2.

Verification

# 1. The yanked version no longer resolves by default
pip index versions geo-core
# expected: 2.4.2 as latest; 2.4.1 absent or marked yanked
# 2. A pin still resolves, deliberately
pip download --no-deps geo-core==2.4.1 -d /tmp/y >/dev/null && echo "pin still works"
# 3. The replacement is correct on every platform, including the broken one
for img in python:3.12-slim python:3.12-alpine; do
  docker run --rm $img sh -c "pip install -q geo-core==2.4.2 && python -c 'import geo_core'" \
    && echo "$img ok"
done

The third check is the release gate that should have run before 2.4.1 — running it now, on the fix, is what stops the sequence repeating. If the validation gate described in testing and validating spatial wheels had covered the failing platform, the release would never have shipped.

Deciding Whether to Yank at All

Yanking is not free — it changes what a resolver does for everyone — so it is worth being deliberate rather than reflexive.

Four kinds of release defect and whether each warrants a yank A wheel that cannot be imported on a supported platform warrants a yank, because the release is unusable for those users. A wheel producing wrong numerical results warrants a yank even more strongly, because the failure is silent. A missing platform is usually not a yank, because the release works for everyone it reaches. And a documentation or metadata error is almost never a yank; publish a fix. cannot import on a supported platform yank — the release is unusable for those users and a fresh install should not land on it produces wrong coordinates yank, urgently — the failure is silent and downstream data may already be affected a platform is missing from the release usually not — it works for everyone it reaches publish the missing wheels in a patch release instead wrong metadata or documentation no — publish a fix; yanking helps nobody here

The second row deserves the emphasis it gets. A wheel that crashes is self-limiting: users notice and stop. A wheel that silently produces coordinates displaced by metres — because a bundled PROJ database mismatched the library, or because two copies of PROJ ended up in one process — keeps producing plausible output that flows downstream. That case warrants a yank, a prominent note, and a clear statement of which versions and which transformations were affected.

The third row is worth resisting. Yanking a release because one platform’s wheel failed to build removes a working release from every other platform, and the resolver’s fallback may be considerably older. Publishing the missing wheels under a new version serves those users better.

Preventing the Next One

Every yank traces back to a gap in the release gate, and the useful follow-up is to close that specific gap rather than to resolve to be more careful.

Four release-gate gaps and the check that closes each An import failure on an untested platform is closed by adding that platform to the validation matrix. A wrong-coordinate defect is closed by a fixed-coordinate fixture asserted in the gate. A missing platform is closed by comparing the collected artifact set against the declared platform list. And a metadata error is closed by running twine check on every wheel before upload. Each gap is closed by a check measured in seconds. import failed on a platform add that platform to the clean-container validation matrix one more image in a loop that already exists coordinates were wrong assert a fixed transform to a fixed tolerance in the gate three lines that would have caught a PROJ data mismatch a platform was missing compare the collected artifacts against the declared platform list a one-line assertion in the fan-in job metadata was rejected or wrong run twine check on every wheel before the upload step the cheapest gate of the four and the most often skipped

Writing the follow-up into the same pull request as the fix is what makes it happen. A release incident generates the motivation to add the check; a week later the motivation is gone and the gap remains. The checks above are each a handful of lines, and between them they cover every defect that has ever justified a yank in this domain.

Pitfalls & Alternatives

Bumping the version to “clean up” instead of yanking. Publishing 2.4.2 without yanking 2.4.1 leaves the broken version installable by anyone whose resolution reaches it, including through a transitive dependency’s range.

Yanking without publishing a fix. Users are pushed onto whatever came before, which may be much older and missing features they depend on. A yank is half an action; the replacement is the other half.

Re-using the version number. The index refuses it, and attempting it in the release job produces a failure that looks like a permissions problem. Version numbers are consumed permanently, including by a release that was never usable.

Deleting the release entirely where the index allows it. It breaks every lock file naming it and removes the evidence of what happened. Yanking exists precisely so that deletion is not the tool.

Frequently Asked Questions

Does yanking notify anyone?

No. Users who pin the version see a warning from some installers, and everyone else simply resolves elsewhere. The notification is the release note and, for a serious defect, whatever channel your users actually read — an issue pinned in the tracker, a mailing list, a post.

What if only the sdist is broken?

Yank the release if the source build is a supported path and it is broken for everyone using it. If it affects only an unusual configuration, a patch release with a note is usually enough — the population is small and can pin.

Can I yank a version and then un-yank it?

Yes on most indexes, and it is worth knowing for the case where the yank turns out to have been unnecessary. Un-yanking restores ordinary resolution; it does not undo the confusion, so it is better to be sure first.

How quickly should the fix follow?

Same day for an import failure, and faster for a wrong-numbers defect. The rebuild is the ordinary release path with one input changed, so the time is dominated by the matrix rather than by the work — which is another argument for keeping the matrix fast.

Should the release notes name the affected platforms precisely?

Yes. “macOS arm64 wheels” is actionable; “some users” is not. Naming the platform lets everyone else stop reading, and lets the affected group act immediately.

Does this interact with attestations or reproducibility?

Only in that the yanked file’s attestation remains valid — it correctly records that a broken artifact came from your pipeline. That is the honest outcome, and it is one more reason to treat the validation gate rather than the signature as the thing that keeps bad artifacts from shipping.

Should the broken version’s release notes be edited?

Add a line saying it was yanked and pointing at the replacement, and leave the rest. Rewriting history makes it harder for someone who installed it to understand what they have, and the note is what a reader who lands there from a search actually needs.

What about a private index without a yank mechanism?

Deleting is usually available there and carries the same risks — every lock file naming the version breaks. The safer equivalent is to publish the fix and add a constraint or a note in your internal package guidance, so the broken version stops being selected without vanishing.

Does a yank affect a package that depends on mine?

Only in resolution: a dependent whose range includes the yanked version resolves to something else. A dependent that pinned it exactly keeps working, which is the property that makes yanking safe to use promptly rather than something to deliberate over.