Skip to content

Vendored scan in a Hatch workspace member writes {root:uri} that the workspace root env never expands, so every hatch run at the root fails to install #505

Description

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

Summary

Hatch 1.16 added workspaces ([tool.hatch.envs.<env>.workspace] members = [...]). A workspace root env installs every member editable and also installs each member's [project] dependencies. For members whose dependencies are static, Hatch takes the raw strings from the member's pyproject.toml (Workspace.get_dependencies → WorkspaceMember.get_dependencies → project.get_dependencies() in hatch/env/plugin/interface.py). No context formatting is applied.

A dependency declared only in a member is refused from the workspace root (redirect_hatch_unsupported, "transitive-only dependencies require agent mode"), so the natural next step is to scan from the member. There, scan --mode vendored rewrites the member's project.dependencies to six @ {root:uri}/.socket/vendor/pypi/<uuid>/six-1.16.0-py2.py3-none-any.whl#sha256=… and reports success, applied: 1. From then on, the workspace root env can't sync:

ERROR: Could not install packages due to an OSError: No connection adapters were found for '{root:uri}/.socket/vendor/pypi/3c4d5e6f-…/six-1.16.0-py2.py3-none-any.whl'

This happens in the existing root env, because the dependency hash changed and Hatch re-syncs, and in every fresh root env, such as a CI checkout. The member's own env (cd libs/lib1 && hatch run …) is fine, because hatchling expands {root:uri} there.

socket-patch already refuses the same "Hatch won't expand {root:uri} here" situation for PEP 735 dependency groups (utils/hatch.rs:232). The member-consumed-by-a-workspace case has no such check.

Hosted mode on the same shape is fine: the absolute URL installs, and a fresh root env imports the patched six.

Impact

After a vendored scan in a workspace member, every hatch run / hatch shell / hatch test in the workspace root env fails, for every developer and in CI, until someone hand-edits the member's pyproject.toml or runs rollback. The scan reports success with no warning.

Repro (Linux, real Hatch 1.18.1 and 1.16.5, main 61cfb9b)

Uses a local mock of the patch API serving six 1.16.0 with a SOCKET_PATCHED marker (the same mock as #335 / #479), with SOCKET_PATCH_SERVER_URL pointed at it.

SP="socket-patch --api-url $API --api-token fake --org test-org"
mkdir -p ws/src/app ws/libs/lib1/src/lib1 && cd ws && touch src/app/__init__.py libs/lib1/src/lib1/__init__.py
cat > pyproject.toml <<'EOF'
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "app"
version = "0.1.0"
dependencies = []

[tool.hatch.build.targets.wheel]
packages = ["src/app"]

[tool.hatch.envs.default.workspace]
members = ["libs/*"]
EOF
cat > libs/lib1/pyproject.toml <<'EOF'
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "lib1"
version = "0.1.0"
dependencies = ["six==1.16.0"]

[tool.hatch.build.targets.wheel]
packages = ["src/lib1"]
EOF
hatch env create
VIRTUAL_ENV=$(hatch env find default) $SP scan --mode vendored --json --yes --ecosystems pypi --cwd libs/lib1
#  -> status success, vendor.summary.applied 1; libs/lib1/pyproject.toml now has "six @ {root:uri}/.socket/vendor/…"
hatch run python -c 'import six'          # existing root env: pip OSError "No connection adapters were found for '{root:uri}/…'"
HATCH_DATA_DIR=$(mktemp -d) hatch run python -c 'import six'   # fresh root env: same error
(cd libs/lib1 && HATCH_DATA_DIR=$(mktemp -d) hatch run python -c 'import six')  # member env: works, patched

From the root, scan --mode vendored refuses as documented (member-only dependency). The same steps with --mode hosted pass.

Expected vs actual

  • Expected: docs/testing/hatch.md says vendored references use {root:uri}, and that a shape where Hatch doesn't expand it is refused before writing ("vendored groups are refused because Hatch does not expand {root:uri} within dependency groups"). Per CLI_CONTRACT.md, a refusal writes nothing and is reported. A member of a Hatch workspace whose static dependencies are consumed by a workspace env should be refused the same way, pointing at hosted or agent mode, or wired so the root can resolve it.
  • Actual: the wiring is written, the scan reports success with no warning, and the workspace root env is broken.

Matrix

OS Hatch Mode Result
Linux 1.18.1 vendored, from member fail (2×: existing and fresh root env)
Linux 1.16.5 vendored, from member fail
Linux 1.18.1 hosted, from member pass (fresh root env patched)
Linux 1.18.1 vendored / hosted, from root refused as documented (member-only dep)
macOS / Windows — — untested. The cause is Hatch's raw read of member metadata, which doesn't depend on the OS.

Hatch < 1.16 has no workspaces. First bad version: not bisected. The latest tag, v4.0.0, predates the Hatch lane (#244).

Suspect code

  • crates/socket-patch-core/src/utils/hatch.rs:206-210: project.dependencies is rewritten with the {root:uri} URL without checking whether the project is a member of an enclosing Hatch workspace. Compare the dependency-group refusal at utils/hatch.rs:232.
  • crates/socket-patch-core/src/vendor/pypi_hatch.rs:172 (wire): it only reads the cwd project's pyproject.toml / hatch.toml. It never looks at ancestor workspace.members.

Activity

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