Skip to content

fix(native): prefer local dev binary over npm package in load order#1389

Merged
carlos-alm merged 15 commits into
mainfrom
fix/native-dev-binary-priority-1361
Jun 8, 2026
Merged

fix(native): prefer local dev binary over npm package in load order#1389
carlos-alm merged 15 commits into
mainfrom
fix/native-dev-binary-priority-1361

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

  • loadNative() now checks for a locally compiled crates/codegraph-core/<platform>.node binary before the published npm package, so Rust changes land immediately in development without a new npm release
  • Adds NAPI_RS_NATIVE_LIBRARY_PATH env-var support as an explicit highest-priority override
  • Extracts resolvePlatformKey() shared helper used by both PLATFORM_PACKAGES and the new PLATFORM_LOCAL_BINARIES map

Load order is now:

  1. NAPI_RS_NATIVE_LIBRARY_PATH env var (explicit override)
  2. crates/codegraph-core/<platform>.node (freshly compiled dev binary)
  3. @optave/codegraph-<platform> npm package (published production binary)

Root cause

PR #1339 added prototype-method extraction to the Rust extractor, but the published npm binary (@optave/codegraph-darwin-arm64 v3.11.2) was built before that merge. In workspaces where the npm package is installed in a parent node_modules/, Node's module resolution finds it before the local binary — so the stale binary was always loaded.

By reversing the priority (local binary first) the integration tests pick up the current Rust code, and Dog.bark is correctly emitted.

Test plan

  • tests/integration/prototype-method-resolution.test.ts — 3/3 passing (was 0/3)
  • Full integration suite (tests/integration/) — 633/633 passing
  • biome check src/infrastructure/native.ts — no errors

Closes #1361

When a locally compiled `crates/codegraph-core/*.node` binary exists it
is now loaded before the published npm platform package.  This ensures
that Rust changes (like the prototype-method extraction from PR #1339)
are picked up immediately in development without waiting for a new
npm release.

Load order is now:
  1. NAPI_RS_NATIVE_LIBRARY_PATH env var  — explicit override
  2. crates/codegraph-core/<platform>.node — freshly compiled dev binary
  3. @optave/codegraph-<platform> npm pkg  — published production binary

Closes #1361
@claude

claude Bot commented Jun 8, 2026

Copy link
Copy Markdown

Claude finished @carlos-alm's task —— View job


PR Review — fix(native): prefer local dev binary over npm package in load order


Phase 1: Root Cause Analysis

Problem Being Solved: When Rust changes are compiled locally but not yet published to npm, the stale @optave/codegraph-* npm package (possibly installed in a parent workspace node_modules/) was always loaded — shadowing the freshly compiled binary. This caused integration tests that depend on the latest Rust extractor output to fail.

Why This Approach: Reversing the priority (local crates/codegraph-core/*.node before npm package) is the minimal, targeted fix. The env-var override adds a clean escape hatch for CI and edge cases. This is a valid approach for a dual-engine tool undergoing active native development.

Risk Assessment: Low. In environments where no local .node binary exists (CI, user installs), the try/catch silently falls through to the npm package path unchanged. No regression risk for production users.


Phase 2: Configuration Change Detection

No linting rules relaxed, no test timeouts changed, no TypeScript strict mode altered, no build configs modified. Pass.


Phase 3: Test Integrity

No test assertions weakened or removed. The PR states 633/633 integration tests passing. The affected test (prototype-method-resolution.test.ts) was not modified — it now passes due to the corrected load order. Pass.


Phase 4: Code Quality

✓ Correct URL path resolution (src/infrastructure/native.ts:112-114): new URL('../../crates/codegraph-core/${localFile}', import.meta.url) — at runtime, import.meta.url resolves to dist/infrastructure/native.js, so ../../crates/codegraph-core/ correctly points to the repo root's crates/codegraph-core/. The math checks out.

✓ Graceful degradation: The local binary path is wrapped in try/catch (lines 111-121) and only logs at debug level on failure — production users see no difference.

resolvePlatformKey() extraction (lines 63-68): Clean refactor — eliminates the duplicated platform/arch/libc computation that previously existed separately in resolvePlatformPackage(). DRY improvement.

⚠ Minor: _loadError may reflect a stale env-var failure (lines 101-104): If NAPI_RS_NATIVE_LIBRARY_PATH is set but fails, _loadError is set to that error. If the local binary then succeeds, _loadError is never cleared (it's still the env-var error). If production falls back to npm and that fails, _loadError gets overwritten correctly (line 130). But if npm also fails and the error message shown to the user via getNative() (line 171) references the env-var failure — that may be misleading. This is low-severity and pre-existing in spirit, but worth noting.

⚠ Minor: PLATFORM_LOCAL_BINARIES comment is slightly misleading (lines 50-52): The comment says "Used as a dev-mode fallback when the npm package is not installed" — but by design this PR makes the local binary first priority, not a fallback. The comment should say "preferred over the npm package in development."

⚠ Minor: No unit test for the new load order: tests/unit/ has no test covering the NAPI_RS_NATIVE_LIBRARY_PATH env-var path or the local-binary priority. The integration test coverage is indirect (it exercises the result, not the mechanism). Not a blocker, but a dedicated unit test mocking the env var would make the behavior contractual.


Phase 5: Backlog Compliance

  • Zero-dep: ✓ — No new runtime dependencies. Uses node:url (built-in), already imported via fileURLToPath.
  • Foundation-aligned: ✓ — Purely local, no cloud calls, no external toolchain. Improves the development inner loop for the native engine.
  • Problem-fit: 2/5 — This is an internal developer ergonomics fix (Rust devs see their changes without publishing). It doesn't directly help AI agents navigate codebases, reduce token waste, or prevent hallucinated deps. Necessary and correct, but not a core feature.
  • Breaking: No — Behavior is unchanged for any environment where crates/codegraph-core/*.node does not exist. The new env-var is purely additive.
  • Tier: 1 — zero-dep ✓ + foundation-aligned ✓.

Critical Concerns

None blocking. The three minor points above are quality suggestions, not correctness issues.


Final Recommendation

Root Cause Analysis
  Problem Being Solved: Stale published npm binary shadowing local Rust build,
                        causing tests of native features to fail.
  Why This Approach:    Minimal priority inversion with graceful fallback — correct.
  Risk Assessment:      Low. No regression for production users.

Backlog Compliance
  Zero-dep:            ✓
  Foundation-aligned:  ✓
  Problem-fit:         2/5 — developer ergonomics fix, not a core user-facing feature
  Breaking:            No
  Tier:                1

Minor Suggestions (non-blocking)
  1. native.ts:50-52  — Update comment: "preferred over npm package" not "fallback"
  2. native.ts:101-104 — Consider clearing _loadError if a subsequent step succeeds,
                          to avoid misleading error messages in getNative()
  3. Add a unit test for NAPI_RS_NATIVE_LIBRARY_PATH env-var and local-binary priority
     (mocking require/fs) to make the load order contractual

Rating: ⭐⭐⭐⭐ (4/5)
Action: APPROVE
Reasoning: The fix is correct, focused, and safe. It solves a real developer pain
           point (Rust changes invisible without publishing), has zero risk for
           production users, and improves code quality via the resolvePlatformKey()
           refactor. The minor issues are polish, not correctness.

@greptile-apps

greptile-apps Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR rewrites the native addon load order in native.ts to prefer a locally compiled dev binary over the published npm package, and adds NAPI_RS_NATIVE_LIBRARY_PATH as a highest-priority explicit override. It also adds class-expression extraction support and fixes scoped rest-param typeMap seeding in the incremental rebuild path.

  • native.ts: Load order is now env-var → local .node file (with existsSync guard) → npm package; local binary failures log at warn level and halt without fallthrough, matching the env-var path semantics.
  • incremental.ts: Seeds callee::restName scoped keys from objectRestParamBindings × paramBindings (Phase 8.3f) and adds two this-receiver fallback resolution blocks to mirror the full-build path.
  • parser.ts / javascript.ts: Adds (class name: ...) tree-sitter patterns for named class expressions in JS/TS, and reclassifies static { } block definitions from kind: 'function' to kind: 'method'.

Confidence Score: 5/5

Safe to merge. All changes are well-scoped, prior review feedback has been incorporated, and new behaviour is covered by integration tests.

The native loader changes are straightforward and the failure semantics are consistent: both the env-var and local-binary paths halt on load failure rather than silently falling through. The incremental builder additions mirror the existing full-build path and are exercised by the new integration test. No regressions are visible in the parser or extractor changes.

No files require special attention.

Important Files Changed

Filename Overview
src/infrastructure/native.ts Implements three-tier load order (env-var → local binary → npm). Previous review feedback fully addressed: env-var failure is fatal, local binary failure is fatal (no fallthrough), JSDoc updated. existsSync guard cleanly distinguishes absent file from load failure.
src/domain/graph/builder/incremental.ts Adds Phase 8.3f scoped typeMap seeding and two this-receiver fallback resolution blocks mirroring the full-build path; integrates caller.callerName into resolveCallTargets call. Logic is consistent and well-commented.
src/domain/parser.ts Adds (class name: ...) tree-sitter query patterns for named class expressions in JS and TS/TSX. Separate node types in tree-sitter-javascript mean no double-matching with class_declaration patterns.
src/extractors/javascript.ts Adds 'class' case to the class-handling switch for class expressions, and corrects static-block kind from 'function' to 'method'. Both changes are consistent with the parser pattern additions.
tests/integration/issue-1369-incremental-rest-param-callerName.test.ts New integration test with two-phase (full build + incremental rebuild) coverage for the scoped rest-param collision scenario. Well-structured with clear fixture and assertions.
tests/parsers/javascript.test.ts Updates existing static-block test to assert kind: 'method' and adds a new describe block for class-expression extraction (extends, methods, super calls, static blocks, field arrow functions).
.github/workflows/claude.yml Bumps claude-code-action pinned commit SHA for both the automated review and interactive assistant jobs.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[loadNative called] --> B{_cached !== undefined?}
    B -- yes --> C[return _cached]
    B -- no --> D{NAPI_RS_NATIVE_LIBRARY_PATH set?}
    D -- yes, success --> E[_cached = module\nreturn module]
    D -- yes, fail --> F[warn log\n_cached = null\nreturn null]
    D -- no --> G{localFile exists\nfor platform?}
    G -- file absent --> I[try npm package]
    G -- file present, success --> H[_cached = module\nreturn module]
    G -- file present, fail --> J[warn log\n_cached = null\nreturn null]
    I -- success --> K[_cached = module\nreturn module]
    I -- fail --> L[debug log\n_cached = null\nreturn null]
    I -- unsupported platform --> M[_loadError = platform error\n_cached = null\nreturn null]
Loading

Reviews (17): Last reviewed commit: "fix(native): warn and halt on local bina..." | Re-trigger Greptile

Comment thread src/infrastructure/native.ts Outdated
Comment on lines +94 to +105
// 1. Explicit path override — highest priority.
const envPath = process.env.NAPI_RS_NATIVE_LIBRARY_PATH;
if (envPath) {
try {
_cached = _require(envPath) as NativeAddon;
debug(`loadNative: loaded from NAPI_RS_NATIVE_LIBRARY_PATH: ${envPath}`);
return _cached;
} catch (err) {
_loadError = err as Error;
debug(`loadNative: NAPI_RS_NATIVE_LIBRARY_PATH load failed: ${toErrorMessage(err as Error)}`);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Explicit env-var failure silently falls through to other loaders

When NAPI_RS_NATIVE_LIBRARY_PATH is set but the load fails (bad path, ABI mismatch, corrupt file, etc.), the code catches the error, logs a debug line, and continues to the local binary and npm package. An explicit override failing silently and loading a different binary is hard to diagnose — the operator set the variable intentionally and has no indication it was ignored. This path should either return null immediately (preserving _loadError so getNative() surfaces the real reason) or at least log at warning level rather than debug. As written, _loadError is also left pointing at the env-var failure even when a subsequent fallback succeeds, creating a misleading error state if _loadError is ever inspected externally.

Fix in Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — when NAPI_RS_NATIVE_LIBRARY_PATH is set but fails to load, we now: (1) log at warn level instead of debug, (2) set _cached = null and return immediately, so no other loader is tried. This ensures an operator who set the variable explicitly gets a visible warning and a clear error from getNative() rather than silently loading a different binary. (commit 183f78e)

Comment on lines +48 to +52
/**
* Map of (platform-arch[-libc]) → locally compiled binary filename.
* Used as a dev-mode fallback when the npm package is not installed,
* e.g. when working with Rust changes that haven't been published yet.
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The JSDoc for PLATFORM_LOCAL_BINARIES says it is a "dev-mode fallback when the npm package is not installed", but the load order now puts it before the npm package — it is checked regardless of whether the npm package is installed. The comment misrepresents the new priority semantics.

Suggested change
/**
* Map of (platform-arch[-libc]) locally compiled binary filename.
* Used as a dev-mode fallback when the npm package is not installed,
* e.g. when working with Rust changes that haven't been published yet.
*/
/**
* Map of (platform-arch[-libc]) locally compiled binary filename.
* Checked before the npm package so that locally compiled Rust changes
* are picked up immediately in development without publishing a new release.
*/

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — updated the JSDoc to 'Checked before the npm package so that locally compiled Rust changes are picked up immediately in development without publishing a new release.' (commit 183f78e)

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Codegraph Impact Analysis

7 functions changed115 callers affected across 40 files

  • buildCallEdges in src/domain/graph/builder/incremental.ts:485 (5 transitive callers)
  • doLoadLanguage in src/domain/parser.ts:204 (6 transitive callers)
  • walkJavaScriptNode in src/extractors/javascript.ts:729 (2 transitive callers)
  • handleStaticBlock in src/extractors/javascript.ts:874 (28 transitive callers)
  • resolvePlatformKey in src/infrastructure/native.ts:65 (59 transitive callers)
  • resolvePlatformPackage in src/infrastructure/native.ts:75 (59 transitive callers)
  • loadNative in src/infrastructure/native.ts:90 (87 transitive callers)

@carlos-alm

Copy link
Copy Markdown
Contributor Author

@greptileai

@carlos-alm

carlos-alm commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

Addressed Claude's review feedback:

  1. FixedPLATFORM_LOCAL_BINARIES JSDoc now reads 'Checked before the npm package so that locally compiled Rust changes are picked up immediately in development without publishing a new release.'
  2. FixedNAPI_RS_NATIVE_LIBRARY_PATH failure now: warns at warn level (not debug), sets _cached = null, and returns immediately. No other loader is tried, so an operator who set the variable gets a clear warning rather than silently loading a different binary. _loadError is no longer left in a stale state if subsequent loaders succeed.
  3. Unit test — Filed test(native): add unit tests for loadNative() load-order and env-var override #1392 to track adding isolated unit tests for loadNative(). The module-level singleton design requires dynamic imports or a reset helper to isolate state, which is better addressed in a dedicated follow-up.

…+ static block + field def

- Add `(class name: ...)` query patterns for JS/TS class expressions so that
  `return class Foo extends Bar { ... }` records the extends relationship in
  ctx.classes — previously only class_declaration was captured, leaving class
  expressions invisible to resolveThisDispatch.

- Add `class_static_block` → `ClassName.<static>` synthetic method definition
  in both query path (extractClassMembersWalk) and walk path (walkJavaScriptNode).
  Calls inside `static { super.f(); }` blocks are now attributed to a method-kind
  node so the CHA parents map can resolve `super.f()` to the parent class.

- Add `field_definition`/`public_field_definition` → `ClassName.fieldName` method
  definition when the field value is an arrow function or function expression.
  `static f = () => { ... }` becomes a resolvable `A.f` node so
  `resolveThisDispatch` can emit the `B.<static> → A.f` edge.

- Mirror all three changes in the native Rust extractor for parity.

- Add 6 parser unit tests and import Jelly micro-test fixtures for super,
  super2, super3, super4, super5 as ground-truth benchmarks.

Benchmark result: super fixture 31% → 38% recall (B.<static> → A.f now resolved).

docs check acknowledged

Closes #1377
…llbacks into buildCallEdges

After resolveCallTargets returns empty:
1. Retry with class-qualified name (ClassName.method) for this-receiver calls
2. Resolve via Object.defineProperty accessor receiver typeMap or same-file lookup

These two blocks existed in buildFileCallEdges (build-edges.ts) but were missing
from the incremental path, causing divergence between full and watch-mode builds.

Also adds the missing callerName argument to resolveCallTargets.

Closes #1384
PLATFORM_PACKAGES[platformKey] was inlined in loadNative() while
getNativePackageVersion() used resolvePlatformPackage(). Any future
change to the lookup (fallback key, default value) would only apply
to one site. Call resolvePlatformPackage() consistently in both places.
@carlos-alm

Copy link
Copy Markdown
Contributor Author

Addressed the remaining Greptile suggestion from the summary:

  • FixedloadNative() step 3 now calls resolvePlatformPackage() instead of inlining PLATFORM_PACKAGES[platformKey] ?? null. Both call sites (loadNative and getNativePackageVersion) now go through the same helper, so any future change to the lookup logic applies everywhere. (commit c3f1b2c)

@carlos-alm

Copy link
Copy Markdown
Contributor Author

@greptileai

@carlos-alm

Copy link
Copy Markdown
Contributor Author

Scope note: The branch unexpectedly contained two commits from other in-flight PRs (#1399, #1401) that were added by a prior session before this sweep started. Specifically:

These could not be removed without a force-push, so they were merged in as-is. The net diff against main now includes those changes alongside the intended native.ts load-order fix. Reviewers should be aware that merging this PR will also land part of the work from those two PRs. If this is undesirable, the branch owner should force-push a clean branch from 183f78e with only the native.ts commits.

…ame in buildCallEdges

Incremental rebuilds using buildCallEdges (incremental.ts) were missing two
things needed for Phase 8.3f scoped-key resolution (#1358 / #1369):

1. The typeMap was not seeded with callee::restName entries from
   objectRestParamBindings × paramBindings. Without this seeding the scoped
   key `callee::restName` (e.g. `f2::rest`) is absent from the map, so
   resolveByMethodOrGlobal's third fallback (`typeMap.get(callerName::receiver)`)
   has nothing to find and the rest-param call goes unresolved.

2. caller.callerName was not passed to resolveCallTargets, so even if the
   scoped key was present in the typeMap (e.g. from WASM extraction), the
   `${callerName}::${effectiveReceiver}` lookup in resolveByMethodOrGlobal
   never fired.

Fix: mirror what buildObjectRestParamPostPass (native full-build post-pass)
and buildCallEdgesJS (WASM full-build path) already do:
- Compute restNameCallees to know how many callees share a rest name.
- Seed typeMap[callee::restName] for each objectRestParamBinding × paramBinding pair.
- Also seed the unscoped key when only one callee uses that rest name, so
  resolution still works when callerName is null (findCaller couldn't match).
- Pass caller.callerName to resolveCallTargets (already present from #1389).

Also syncs call-resolver.ts and build-edges.ts with the scoped-key changes
from PR #1368 (merged to main separately), which this branch was missing.

docs check acknowledged

Closes #1369
…or static blocks (#1389)

The merge conflict resolution in 8d5ba14 dropped two changes from ca3123f:
1. The 'class' case in walkJavaScriptNode's switch — class expressions like
   'return class Child extends Parent { ... }' were never dispatched to
   handleClassDecl, so ctx.classes remained empty and no extends relationship
   was recorded.
2. handleStaticBlock used kind: 'function' instead of kind: 'method', so the
   CHA parents map could not walk up to the parent class for super.method()
   resolution. Also update the existing test that asserted kind: 'function'.
@carlos-alm

Copy link
Copy Markdown
Contributor Author

Fixed CI failures introduced during merge conflict resolution:

  1. The 'class' case was dropped from walkJavaScriptNode's switch in the conflict resolution of 8d5ba14. Class expressions ('return class Child extends Parent { ... }') were never dispatched to handleClassDecl, leaving ctx.classes empty. Added 'case class':' back.

  2. handleStaticBlock used kind: 'function' instead of the intended kind: 'method'. The correct value is 'method' so the CHA parents map can walk up to the parent class to resolve super.method() calls. Updated the existing test at line 109 (which asserted kind: 'function') to match the correct behavior.

Also merged origin/main to resolve branch divergence (3 commits: jelly-micro fixture import, Phase 8.3f typeMap scoping, vitest bump). Schema path conflict in super/expected-edges.json resolved by taking main's '../../../' path which is correct for the fixture's directory depth.

@carlos-alm

Copy link
Copy Markdown
Contributor Author

@greptileai

@carlos-alm

Copy link
Copy Markdown
Contributor Author

Addressed Greptile's diagnostic note on getNativePackageVersion() — added a JSDoc note clarifying that this function always reports the npm package version, so when the local dev binary or NAPI_RS_NATIVE_LIBRARY_PATH is loaded instead, the reported version may not match the running binary. (commit 612bde0)

…eclaration (#1389)

- native.ts: add debug log when step 3 (npm package) loads successfully,
  matching the trace already present for steps 1 and 2
- incremental.ts: split `let { targets, importedFrom }` into
  `const { targets: initialTargets, importedFrom }` + `let targets = initialTargets`
  to make explicit that only targets is ever reassigned
@carlos-alm

Copy link
Copy Markdown
Contributor Author

Addressed the two remaining items from Greptile's latest summary:

  1. native.ts:132-137 — Added debug('loadNative: loaded npm package: ${pkg}') on success in step 3, matching the trace already emitted by steps 1 and 2. When the npm package loads successfully, the log now shows which path was taken. (commit ab8a300)

  2. incremental.ts:541-548 — Split let { targets, importedFrom } into const { targets: initialTargets, importedFrom } + let targets = initialTargets. Only targets is ever reassigned; importedFrom retains its initial value. Separating the declarations makes the mutability intent explicit. (commit ab8a300)

@carlos-alm

Copy link
Copy Markdown
Contributor Author

@greptileai

@carlos-alm

Copy link
Copy Markdown
Contributor Author

Fixed — aligned lockfile to vitest 4.1.8 to match main. The 4.1.7 entries for @vitest/coverage-v8, @vitest/pretty-format, and @vitest/utils were a merge artifact from the branch being cut before the vitest bump landed. (commit 5e0c500)

@carlos-alm

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment on lines +113 to +127
// 2. Locally compiled dev binary — preferred over npm package so that Rust
// changes are visible without publishing. Only used when the file exists.
const localFile = PLATFORM_LOCAL_BINARIES[platformKey];
if (localFile) {
try {
const localPath = fileURLToPath(
new URL(`../../crates/codegraph-core/${localFile}`, import.meta.url),
);
_cached = _require(localPath) as NativeAddon;
debug(`loadNative: loaded local dev binary: ${localPath}`);
return _cached;
} catch (err) {
debug(`loadNative: local dev binary not available: ${toErrorMessage(err as Error)}`);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Local binary load failure silently falls through to the npm package

When the local dev binary exists on disk but fails to load (e.g., a stale .node file from a prior build after an ABI change), the catch block logs at debug level and falls through to the npm package — the exact scenario the PR is trying to prevent. A developer would see their Rust changes "tested" but the integration suite would actually run against the stale npm binary, potentially giving a false-green.

The env-var path (step 1) already distinguishes "explicit operator choice, failure is fatal" from general failures. The same distinction is worth making here: err.code === 'MODULE_NOT_FOUND' means the file simply isn't there (normal in CI/production, silent skip is correct); any other error means the file exists but failed to load — that case should log at warn level and ideally return null rather than fall through.

Fix in Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — when a local binary exists on disk but fails to load, we now use existsSync to distinguish the two cases: if the file is absent, fall through normally to the npm package; if the file is present but require throws, log at warn level and return null immediately (no fallthrough). This mirrors the same pattern used for NAPI_RS_NATIVE_LIBRARY_PATH failures. Commit: f57ad9c.

carlos-alm and others added 2 commits June 8, 2026 12:28
…llthrough

When a local dev binary exists on disk but fails to load (e.g. stale ABI
after a Rust rebuild), warn at warn level and return null immediately.
Only fall through to the npm package when the file is absent entirely.
This preserves the priority guarantee: a present-but-broken local binary
surfaces as an explicit developer error rather than silently running the
published npm binary.
@carlos-alm

Copy link
Copy Markdown
Contributor Author

@greptileai

@carlos-alm carlos-alm merged commit eacc2c8 into main Jun 8, 2026
26 checks passed
@carlos-alm carlos-alm deleted the fix/native-dev-binary-priority-1361 branch June 8, 2026 21:06
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 8, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(native): prototype method extraction binary not released — Dog.bark missing from native engine

1 participant