Skip to content

[BUG] Parallel sparse-checkout race condition when two subdirectory deps reference the same repository #1126

@itayozer9

Description

@itayozer9

Describe the bug

When an apm.yml declares two (or more) virtual subdirectory dependencies that reference the same GitHub repository (e.g. org/repo/shared and org/repo/plugins), the parallel resolution introduced in v0.12.1 causes a race condition. Both dependencies attempt to sparse-checkout the same cloned repo concurrently, but only one subdirectory path is configured — the other always fails with:

Failed to download dependency <org>/<repo>: Subdirectory '<subdir>' not found in repository

Each subdirectory dependency works perfectly in isolation. The failure only occurs when both are declared together and resolved in parallel from a clean state (no cached clone).

To Reproduce

  1. Create a GitHub repo (e.g. org/my-apm-packages) with two subdirectory packages, each containing its own apm.yml:
my-apm-packages/
├── apm.yml              # root package
├── shared/
│   ├── apm.yml          # name: my-shared
│   └── .apm/
│       └── skills/
│           └── skill-a/
└── plugins/
    ├── apm.yml          # name: my-plugins
    └── .apm/
        └── skills/
            └── skill-b/
  1. In a consumer project, declare both subdirectory deps in apm.yml:
dependencies:
  apm:
  - org/my-apm-packages/shared
  - org/my-apm-packages/plugins
  1. Ensure a clean state (no cached clone):
rm -rf apm_modules apm.lock.yaml
  1. Run:
apm install --verbose
  1. Observe that one dependency installs successfully while the other fails:
[>] Resolving my-apm-packages-shared...
[>] Resolving my-apm-packages-plugins...
  [i] github.com -- token from git-credential-fill
  Failed to download dependency org/my-apm-packages: Subdirectory 'shared' not found in repository
  1. Run apm install a second time — it now succeeds because the first run cached the full clone and the second run reuses it:
[+] github.com/org/my-apm-packages/shared @abc1234
[+] github.com/org/my-apm-packages/plugins @abc1234
[*] Installed 2 APM dependencies in 12.1s.
  1. Confirm the bug is specific to multi-subdirectory — changing apm.yml to reference only one subdirectory at a time always succeeds on a clean install.

Expected behavior

Both subdirectory dependencies from the same repository should resolve successfully on the first apm install from a clean state. The parallel sparse-checkout dedup logic should either:

  • Clone the repo once and then check out both subdirectories sequentially, or
  • Serialize sparse-checkout operations for deps sharing the same repo clone

Environment (please complete the following information):

  • OS: macOS 15.7.4 (Sequoia)
  • Python Version: 3.14.0
  • APM Version: 0.12.1 (41ab121)
  • Node Version: 24.6.0
  • Git Version: 2.49.0

Logs

First install (clean state) — fails:

[>] Installing dependencies from apm.yml...
Parsed apm.yml: 2 APM deps, 0 MCP deps
[>] Resolving my-apm-packages-shared...
[>] Resolving my-apm-packages-plugins...
  [i] github.com -- token from git-credential-fill
  Failed to download dependency org/my-apm-packages: Subdirectory 'shared' not found in repository
Resolved 2 direct dependencies (no transitive)
Phase: resolve -> 10.252s
  Skipping org/my-apm-packages/shared (already failed during resolution)
  [+] github.com/org/my-apm-packages/plugins @abc1234
  |-- 5 skill(s) integrated -> .agents/skills/
  [x] 1 package failed:
    +- my-apm-packages-shared -- Failed to download dependency org/my-apm-packages: Subdirectory 'shared' not found in repository
[!] Installed 1 APM dependency in 22.1s with 1 error(s).

Second install (cached clone) — succeeds:

[>] Installing dependencies from apm.yml...
Parsed apm.yml: 2 APM deps, 0 MCP deps
Using apm.lock.yaml (1 locked dependencies)
[>] Resolving my-apm-packages-plugins...
[>] Resolving my-apm-packages-shared...
  [+] github.com/org/my-apm-packages/shared @abc1234
  |-- 2 skill(s) integrated -> .agents/skills/
  [+] github.com/org/my-apm-packages/plugins @abc1234
  |-- 5 skill(s) integrated -> .agents/skills/
[*] Installed 2 APM dependencies in 12.1s.

Additional context

  • This is a regression from APM 0.9.4, which resolved dependencies sequentially and handled this case correctly.
  • The issue was likely introduced by the "parallel level-batched BFS dependency resolution" and "in-install repo clone dedup for subdirectory deps" changes in v0.12.1.
  • The race is non-deterministic in which subdirectory fails, but in practice the second one to start its sparse-checkout always loses because the first has already configured the sparse-checkout cone for only its own path.
  • Workaround: Run apm install twice — the first run populates the clone cache, and the second succeeds. Alternatively, restructure to use a single root-level .apm/ directory so consumers only need one dependency entry per repository.

Metadata

Metadata

Assignees

No one assigned

    Labels

    type/bugSomething does not work as documented.

    Type

    No type

    Projects

    Status

    In Progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions