Skip to content

Agent-mode cargo with HOME unset finds no registry cache, reports the crate as lockfile-only and exits 0, while cargo builds the unpatched copy from the passwd home's ~/.cargo #1135

Description

[agent] Found by the scheduled Cargo bug-hunt routine (ledger #315).

Summary

When HOME is unset and CARGO_HOME is unset, cargo still finds its home: the home crate falls back to the passwd entry (getpwuid(getuid())->pw_dir), so it builds from /root/.cargo (or /home/<user>/.cargo). socket-patch's cargo crawler resolves CARGO_HOME, then utils::fs::home_dir(), which since #1038 reads only HOME / USERPROFILE and returns None otherwise, so it probes no registry at all. An agent scan --mode agent then sees the patched crate only in Cargo.lock (lockfileOnlyPackages: 1), patches nothing, and exits 0 with status: "success". The next cargo build compiles the unpatched registry copy.

Impact

Repro (Linux, cargo 1.93.1, root user, passwd home /root)

mkdir -p proj/src && cd proj
printf '[package]\nname = "consumer"\nversion = "0.1.0"\nedition = "2018"\n\n[dependencies]\ncfg-if = "1.0.4"\n' > Cargo.toml
echo 'fn main() {}' > src/main.rs
cargo generate-lockfile && cargo update -p cfg-if --precise 1.0.4 && cargo fetch
env -u HOME -u CARGO_HOME cargo metadata --format-version 1 | jq -r '.packages[] | select(.name=="cfg-if").manifest_path'
#   /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cfg-if-1.0.4/Cargo.toml   <- cargo still uses ~/.cargo

# patch API stand-in serving one free patch for pkg:cargo/cfg-if@1.0.4 (appends `pub fn socket_patched()` to src/lib.rs)
env -u HOME -u CARGO_HOME socket-patch scan --mode agent --json --yes --api-url $MOCK --org test-org --api-token fake
#   status: success, scannedPackages: 1, lockfileOnlyPackages: 1, totalPatches: 1, events: [], exit 0
grep -c socket_patched /root/.cargo/registry/src/*/cfg-if-1.0.4/src/lib.rs     # 0: not patched

# control, same project, HOME=/root:
socket-patch scan --mode agent --json --yes ...       # scannedPackages: 233, lockfileOnlyPackages: 0, the copy is patched

It reproduced on two consecutive runs (identical output), and the control with HOME set patches the copy every time.

Expected vs actual

  • Expected: agent mode patches "the crate wherever the crawler finds it" (docs/ecosystems.md, Cargo row) in the registry cache cargo actually builds from. When CARGO_HOME is unset, that's the default cargo home, <home>/.cargo, with <home> resolved the way cargo resolves it (HOME, then the passwd entry on Unix). utils::fs::home_dir's own doc already lists readers that "deliberately follow another tool's own rule" (Gradle's passwd-vs-$HOME check in gradle/home.rs), so cargo needs the same treatment. Failing that, the run should at least say that no cargo home was found, instead of passing as lockfile-only.
  • Actual: no home, so no registry probe, the crate is reported lockfile-only, status: success, exit 0, and the build is unpatched.

Matrix

OS cargo HOME CARGO_HOME Reproduces
Linux 1.93.1 unset unset yes (×2)
Linux 1.93.1 /root unset no (control)
Linux 1.93.1 unset set no (the crawler uses CARGO_HOME)
macOS any unset unset untested (same code path; cargo's passwd fallback is the same)
Windows any USERPROFILE unset unset untested (cargo's home crate uses the known-folder API there)

Tested on main 9472be4 (CLI 4.0.0). Not a regression from #1038: before it, home_dir fell back to the CWD-relative path ~ (that commit's message), which also found no registry. #1038 just made "no home" explicit.

Suspect code

  • crates/socket-patch-core/src/crawlers/cargo_crawler.rs:355-360 (CargoCrawler::cargo_home): CARGO_HOME, else utils::fs::home_dir().map(|h| h.join(".cargo")), with no passwd fallback.
  • crates/socket-patch-core/src/utils/fs.rs:387-398 (home_dir / home_from_env): HOME / USERPROFILE only.
  • The same CARGO_HOME-or-home_dir rule is in crates/socket-patch-core/src/vendor/cargo_config.rs:156-158 (read_config_chain). With HOME unset, the $CARGO_HOME/config.toml that cargo still reads (a user [patch] / [source]) is skipped by the vendored and hosted config-chain checks too. I didn't drive that path end to end.

Related: #616 (the same lockfile-only exit 0 when the cache is genuinely cold). A fix there that makes a lockfile-only cargo patch loud would surface this case, but it still wouldn't patch the copy cargo builds.

Activity

  1. added a commit that references this issue on Oct 8, 2026
  2. mikolalysenko commented on Oct 8, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triage: priority:p2 (Cargo). Confirmed on main 3b4ac84: CargoCrawler::cargo_home (crates/socket-patch-core/src/crawlers/cargo_crawler.rs:355) falls back only to utils::fs::home_dir(), which reads HOME/USERPROFILE and has no passwd fallback, so with both HOME and CARGO_HOME unset no registry is probed. Related to #616 (lockfile-only exit 0 on a cold cache) but a different cause, so not a duplicate. No open PR covers it.


    Generated by Claude Code

  3. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Cargo bug-hunt run 17 (ledger #315), main 4aec9d7, Linux, cargo 1.93.1.

    • The agent-mode repro still fails ×2 after Scope the project-mode cargo crawl to the crates Cargo.lock resolves (#1204) #1205: success, lockfileOnlyPackages: 1, applied 0.
    • New variant, global mode: env -u HOME -u CARGO_HOME socket-patch scan -g -e cargo prints "No global packages found." and exits 0 (--json: scannedPackages: 0, no warning). With HOME=/root the same command crawls 223 crates from the passwd home's ~/.cargo, the cache cargo itself uses. The root cause is the same: CargoCrawler::cargo_home() returns None when home_dir() has no HOME/USERPROFILE, and nothing reports it. A global run should at least warn that no cargo home could be resolved.

    Generated by Claude Code

  4. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    v5 triage: P3, not a release blocker. HOME and CARGO_HOME both unset is an unusual launch environment. P3 rather than a release gate for ordinary Cargo lockfiles.

    This follows the maintainer's release scope: one normally completing CLI instance, prioritizing valid-lockfile patch/install behavior, compatibility, and actionable CLI UX.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions