Repository navigation
Hosted cargo redirect refuses a crates.io dependency declared with an explicit registry = "crates-io", treating crates.io as "another registry" #386
Description
[agent] Found by the scheduled Cargo bug-hunt routine (ledger #315).
Summary
scan --mode hosted skips a patched crate when its Cargo.toml declaration names the default registry explicitly: cfg-if = { version = "1.0.4", registry = "crates-io" }, or registry = "crates-io" in a [dependencies.cfg-if] table. It warns redirect_cargo_toml_dep_unrewritable … cannot be pinned (pinned to another registry ("crates-io")); dependency skipped (nothing rewritten), reports redirected: 0, and exits 0 with status: "success".
crates-io is Cargo's built-in name for crates.io, so this dependency is exactly the crates.io dependency hosted mode is built to redirect. Cargo resolves it identically to a declaration with no registry key: the baseline cargo build --locked succeeds, and the lock entry is source = "registry+https://github.com/rust-lang/crates.io-index". The refusal fires when it shouldn't.
Impact
Hosted mode never pins the patch for this dependency, so the project keeps building the unpatched crate. The only signal is a warning inside a success envelope. It fails closed: nothing is rewritten and nothing unpatched is attested. Vendored mode handles the same declaration correctly (vendor applies it, and cargo run --locked --offline links the patched marker), so the gap is hosted-only. Workaround: delete the redundant registry = "crates-io".
Repro
This uses the existing wiremock sparse-registry harness in crates/socket-patch-cli/tests/e2e_redirect_cargo_shapes.rs. Add a shape identical to cargo_hosted_legacy_config_is_restored_byte_for_byte, minus the config file, with this dependency line:
("Cargo.toml", consumer_manifest("cfg-if = { version = \"1.0.4\", registry = \"crates-io\" }\n")),
// or: consumer_manifest("") + "\n[dependencies.cfg-if]\nversion = \"1.0.4\"\nregistry = \"crates-io\"\n"cargo test -p socket-patch-cli --test <file> then fails at the every patch redirected assertion:
"redirect":{"mode":"hosted","redirected":0,"rewrittenFiles":[],"skipped":[],
"warnings":[{"code":"redirect_cargo_toml_dep_unrewritable",
"detail":"cfg-if in Cargo.toml cannot be pinned (pinned to another registry (\"crates-io\")); dependency skipped (nothing rewritten)"}]}
The baseline steps before the scan pass: cargo generate-lockfile, and cargo build --locked with that manifest on cargo 1.93.1.
Expected vs actual
- Expected: docs/ecosystems.md (Cargo row) says hosted mode redirects direct crates.io dependencies through a per-patch sparse registry (
[registries.socket-patch-<uuid>]plus Cargo.lock source/checksum). A dependency whoseregistryiscrates-iois a direct crates.io dependency, so the rewriter should replace the value withregistry = "socket-patch-<uuid>", the same way it already replaces a stalesocket-patch-*value. - Actual: it's refused as "another registry", and nothing is redirected.
Matrix
| OS | cargo | inline { …, registry = "crates-io" } |
[dependencies.x] table |
control: no registry key |
|---|---|---|---|---|
| Linux | 1.93.1 | fail (2/2 runs) | fail (2/2 runs) | pass |
| macOS / Windows | — | not probed: a pure text rewriter with no OS-specific branch |
In the same run, these shapes passed on Linux: optional = true with a [features] reference (both "cfg-if" and "dep:cfg-if"), default-features = false, a "1" caret requirement, a workspace where one member renames the dep, an idempotent re-scan, and cargo vendor after the redirect.
Suspect code
crates/socket-patch-core/src/patch/redirect/mod.rs:2547-2569 (table form) and :2665-2682 (inline form). A registry value other than the target or an is_socket_patch_registry_name value is refused. "crates-io" should be handled like a socket-patch name (replace the value) instead. The registry-index refusal next to it is a separate case, and it's fine.
Tested on main f6b7fb9 (CLI 4.0.0). I didn't bisect: the hosted cargo rewriter has had this branch since hosted mode shipped.
Activity
cargo: run 2 ledger (filed #386, #387)
mikolalysenko commentedon Sep 30, 2026
[agent] Triaged:
priority:p2(Cargo). Not a duplicate, and no open or merged PR fixes it. On main, the Cargo.toml redirect rewriter inpatch/redirect/mod.rs(theregistrykey arms around lines 2546-2680) refuses anyregistryvalue that is not asocket-patch-*name, without special-casing Cargo's built-incrates-ioname.Generated by Claude Code
mikolalysenko commentedon Oct 1, 2026
[agent] Re-triage on main
2463257(after the v5 consolidation in #277): still reproduces on cargo 1.93.1.I used the wiremock harness from
tests/e2e_redirect_cargo_shapes.rswithcfg-if = { version = "1.0.4", registry = "crates-io" }.scan --mode hostedreturnsredirected: 0withredirect_cargo_toml_dep_unrewritable: "cfg-if in Cargo.toml cannot be pinned (pinned to another registry ("crates-io")); dependency skipped (nothing rewritten)". The refusal now comes fromcrates/socket-patch-core/src/patch/redirect/mod.rs:2546(table form) and:2659(inline). Neither treats the built-incrates-ioname as crates.io, while vendored mode does (vendor/cargo_manifest.rs:84,is_crates_io_source).Generated by Claude Code
mikolalysenko commentedon Oct 12, 2026
[agent] Claiming this issue (shared root cause: the hosted Cargo.toml planner treats Cargo's built-in
crates-ioregistry name as a foreign registry). Branch: agent/fix-cargo-crates-io-registry-pin. Claim-ID: 2026-10-12T02:21:19Z-462edfGenerated by Claude Code
mikolalysenko commentedon Oct 12, 2026
[agent] Draft PR: #1412
Generated by Claude Code
Pin cargo deps that name registry crates-io
Keep #1020 takeover test on a refused spelling
Keep comments when restoring cargo pin lines
Keep CRLF on a restored cargo comment line
chore(release): 5.0.1 (#1485)