Skip to content

Archives from separate cargo invocations can bundle different tokio builds off one Cargo.lock; the link guard catches it but two agents hit it today in unrelated work #10671

Description

@proggeramlug

Two independent agents hit this today, hours apart, in unrelated work. Filing it as a build-system issue
because it is not a one-off and it produces a confusing failure.

The problem

Two static archives built in separate cargo invocations can carry different tokio compilations even off an
identical Cargo.lock
. Linking them together then fails.

Perry's own linker detects and refuses this (crates/perry/src/commands/compile/library_search.rs,
runtime_compat.rs link guard), which is good — it fails loudly rather than producing a subtly broken binary. But
the error surfaces far from the cause, and the fix is non-obvious unless you already know the rule.

The two sightings

  1. Package-compilability probe. Compiling pg auto-triggered a perry-ext-pg build (it bridges to
    sqlx::postgres + tokio) in its own cargo invocation; the resulting libperry_ext_pg.a bundled a different
    tokio than the existing libperry_stdlib.a. Resolved by rebuilding coherently:
    cargo build --profile perry-dev -p perry -p perry-runtime-static -p perry-stdlib-static -p perry-ext-pg
  2. PR fix(http): rebuild client-pump stdlib for node:http under PERRY_NO_AUTO_OPTIMIZE #10667. Its first version rebuilt perry-stdlib-static without perry-ext-http, tripped the
    runtime_compat.rs guard, and had to rebuild both together in one invocation.

Why it is worth fixing rather than documenting

CLAUDE.md already warns that dropping a -p changes cargo feature unification, and the fix-agent brief tells
agents to keep the package set identical across builds. Both sightings happened anyway, to agents that had read
those warnings — because the failure mode is triggered by an auto-triggered build (case 1) or by a reasonable
partial rebuild (case 2), not by carelessness.

Options worth weighing: have the ext-crate auto-build join the parent invocation rather than spawning its own;
have the link guard's error name the required cargo build -p ... line explicitly; or record a build fingerprint
in each archive so the mismatch is reported as "built in a different invocation" rather than a tokio symbol clash.

Related

The same family as the stale-.a trap CLAUDE.md documents under "Verifying a runtime change" — where rebuilding
the rlib crates without the -static wrappers silently links a stale archive and makes both arms of an A/B behave
identically. That one produces a vacuous pass; this one produces a confusing failure. Both come from archives
and invocations not lining up.

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

    package-auditFound by the 2026 package audit: compiling real npm packages from source instead of native bindings

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions