Skip to content

ethers fails at link: undefined references from a re-export-from-builtin shape (export { createHash, ... } from "crypto"), now reachable after #10757 #10802

Description

@proggeramlug

With the HIR-lowering blowup fixed (#10757 / PR #10801), ethers 6.17.0's full 153-module dependency tree now lowers and generates code in about a minute. It then fails at the link step on undefined references.

Found immediately after #10757's fix, on origin/main + PR #10801, Linux x64.

The shape

The undefined symbols come from ethers/src.ts/crypto/crypto.ts, which re-exports node builtins:

export { createHash, createHmac, pbkdf2Sync, randomBytes } from "crypto";

plus one WebSocket wrapper symbol.

So this is an export { … } from "<node builtin>" pass-through — the module re-exports names it never imports into its own scope — and those symbols are not emitted or resolved at link time.

Why it is filed separately

It is unrelated to #10757: that was an exponential blowup in class_mutates_capture's recursion, entirely within HIR lowering. This is a linker-visible symbol-emission problem that only became reachable because lowering now completes. The #10757 fix is correct and complete for what it claims; this is the next thing in the way.

That is also the expected pattern for this campaign — each blocker removed exposes the next. It is the removals doing their job, not evidence the previous fix was wrong.

Reproduction

mkdir ethers-probe && cd ethers-probe
npm install ethers@6.17.0
# package.json: { "perry": { "compilePackages": ["ethers"] } }
# fixture.ts: import { Wallet } from "ethers"; ...
perry compile fixture.ts -o fixture    # lowers + codegens, then fails at link

Suggested first check

Whether a minimal export { createHash } from "crypto"; inside a compiled package reproduces it without ethers. If it does, the defect is in the re-export-from-builtin path generally, and is likely to affect any package wrapping node crypto — a common shape in anything that hashes or signs.

Activity

  1. added
    package-auditFound by the 2026 package audit: compiling real npm packages from source instead of native bindings
    on Sep 20, 2026
  2. proggeramlug commented on Sep 20, 2026

    @proggeramlug
    ContributorAuthor

    This is almost certainly #10432, which already has a six-line reproducer — so this does not need ethers checked out to work on.

    #10432 ("Re-exporting a Node builtin's named export through a local module is broken", open, found 2026-09-06 on v0.5.1587, Linux x64) reports exactly the shape ethers hits:

    the export { x } from "<builtin>" form produces an undefined perry_fn_<module>__<x> symbol at link time, and the import { x } from "<builtin>"; export { x } form links but the imported value returns undefined when called.

    ethers/src.ts/crypto/crypto.ts is export { createHash, createHmac, pbkdf2Sync, randomBytes } from "crypto" — the first form — and the undefined symbols here are perry_fn_..._crypto_ts__createHmac / pbkdf2Sync / randomBytes / createHash. Same form, same symbol shape, same builtin.

    Its case 1 is:

    echo 'export { createHash } from "crypto";' > dep.ts
    # main.ts imports createHash from ./dep.ts

    That is a much better starting point than a 153-module dependency tree, and it also records the second form's distinct failure — links fine, returns undefined at call time — which this issue would not have surfaced on its own, because ethers never gets past the link.

    Worth cross-linking rather than merging outright, since this issue carries something #10432 does not: the real-world blast radius, and the WebSocket wrapper symbol, which may or may not be the same root cause. If it turns out to be, closing this as a duplicate of #10432 and carrying the ethers acceptance case over there is probably the right end state.

    Context on the other half: #10801 (the class_mutates_capture memoization) is queued for a merge train and should land shortly, at which point ethers lowers and codegens — 153 modules, 0 JS fallback, about a minute — and this link failure becomes the only thing between that and a working build. So this is now the single blocker for ethers, not one of two.

    #10757 should not be closed on #10801 alone. "Compiles" is not "links", and closing it would lose the thread from the original never-finishes report to this.

  3. proggeramlug commented on Sep 21, 2026

    @proggeramlug
    ContributorAuthor

    Fixed in v0.5.1632 (merge train 252, 841b605c97) by #10867 — fix(modules): preserve named Node builtin re-exports. Regression test crates/perry-hir/tests/node_named_export_hygiene.rs.

    Train validation: 173 gap fixtures across seven areas with zero regressions, re-run against the current base after v0.5.1631 changed canonical_dir's path resolution. All ratchets, cargo fmt, and cargo check --workspace --all-targets under -D warnings clean.

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