Skip to content

fix: say which package is missing when the native binding fails#168

Merged
jan-kubica merged 2 commits into
mainfrom
fix/native-binding-diagnostics
Jul 26, 2026
Merged

fix: say which package is missing when the native binding fails#168
jan-kubica merged 2 commits into
mainfrom
fix/native-binding-diagnostics

Conversation

@jan-kubica

@jan-kubica jan-kubica commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

What changed

The native loader now distinguishes between:

  • an unsupported platform; and
  • a supported target whose optional native package could not be loaded.

For supported targets, the error names the expected package and preserves the detailed Tried: diagnostics. The loader also keeps the explicit native-library override ahead of package resolution.

Tests

Added coverage for supported and unsupported targets, missing optional packages, preserved resolution diagnostics, and explicit path precedence.

Release

Includes a patch changeset for @stll/stdnum.

@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The native binding loader now distinguishes unsupported targets from build targets with missing optional packages, formats attempted-load errors, and has tests covering diagnostics and explicit library-path precedence.

Changes

Native binding diagnostics

Layer / File(s) Summary
Loader error reporting
packages/stdnum/src/native-node.ts
Builds current and supported target identifiers, selects target-specific failure reasons, and formats attempted package errors.
Loader behaviour tests
packages/stdnum/src/__test__/native-node.test.ts
Simulates missing native packages and verifies error wording, target classification, unsupported targets, and explicit library-path loading.
Release metadata
.changeset/loud-pandas-repeat.md
Adds a patch changeset describing the missing optional-package error behaviour.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: improving native binding failure messages to identify the missing package.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/native-binding-diagnostics

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/stdnum/src/__test__/native-node.test.ts (1)

24-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the preserved Tried: diagnostics.

This only verifies the package name, which is also in the high-level reason. Assert the Tried: header and an indented attempted resolution so the stated diagnostic contract cannot regress.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/stdnum/src/__test__/native-node.test.ts` around lines 24 - 34,
Update the test around loadNativeStdnumBinding to assert the preserved “Tried:”
diagnostics, including the header and an indented attempted resolution entry,
while retaining the existing package-name assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/stdnum/src/__test__/native-node.test.ts`:
- Around line 77-80: Document the unavoidable NativeStdnumBinding cast in the
Proxy setup by adding a // SAFETY: comment explaining that the Proxy returns
functions for every runtime contract check performed by asNativeBinding. Keep
the existing Proxy behavior and cast unchanged.

In `@packages/stdnum/src/native-node.ts`:
- Around line 86-92: Update the reason construction in the native module loading
path to describe a matched package as one that “could not be loaded,” rather
than asserting it was not installed. Keep the unsupported-platform message for
match === undefined, and leave the specific requireModule failure cause to the
existing “Tried:” details.

---

Nitpick comments:
In `@packages/stdnum/src/__test__/native-node.test.ts`:
- Around line 24-34: Update the test around loadNativeStdnumBinding to assert
the preserved “Tried:” diagnostics, including the header and an indented
attempted resolution entry, while retaining the existing package-name assertion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 28f06f13-d063-40e4-9a1a-325efde0702f

📥 Commits

Reviewing files that changed from the base of the PR and between a36a98d and b6e9f91.

📒 Files selected for processing (2)
  • packages/stdnum/src/__test__/native-node.test.ts
  • packages/stdnum/src/native-node.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Bindings and packages
  • GitHub Check: Pack
  • GitHub Check: Oracle
  • GitHub Check: Rust
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{rs,ts,tsx,py}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Keep Node.js, WASM, and Python bindings as thin adapters over the single Rust implementation; do not hand-write validator logic in bindings.

**/*.{rs,ts,tsx,py}: Separate validation status from presentation: kernels return compact statuses and typed error codes; bindings construct user-facing strings, objects, exceptions, and diagnostics only when required.
Keep binding calls coarse; prefer indexed dispatch and batch operations to amortize Node and Python FFI crossings.
Treat performance as a tested product contract with deterministic allocation assertions and controlled normalized oracle benchmarks; do not use noisy wall-clock unit checks.

Files:

  • packages/stdnum/src/native-node.ts
  • packages/stdnum/src/__test__/native-node.test.ts
**/*.{ts,tsx,py,json,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

After adding or changing a validator, use bun run codegen to generate npm entrypoints, TypeScript and Python registry types, package exports, and README tables; do not manually maintain generated outputs.

Files:

  • packages/stdnum/src/native-node.ts
  • packages/stdnum/src/__test__/native-node.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Prefer explicit values and thread backend discriminators through URL parameters, component props, and the full TypeScript frontend stack instead of hardcoding defaults.
Prefer TypeScript types that make invalid states structurally impossible, including branded types, discriminated unions, and exhaustive checks.
Use named discriminators or domain types instead of boolean fields for states that may grow.
Fail fast: validate at boundaries and return or throw early; minimize brace nesting with inverted conditions and early returns.
Use named constants instead of string literals for domain values.
Do not assign directly to document.cookie.
Avoid spread syntax in loop accumulators; use .push().
Do not use TypeScript enums; use as const objects or union types.
Model mutually exclusive states as discriminated unions with a stable discriminator; avoid boolean flags combined with optional payload fields.
Avoid as casts; narrow with type guards, in checks, or records. If unavoidable, add a // SAFETY: comment explaining why it is sound.
Trace type mismatches to their source rather than casting at the consumer, and verify whether the change introduced the mismatch.
Do not annotate values the compiler already infers or pass explicit type arguments to inference-driven hooks and API calls.
Validate large-union object literals with as const satisfies T rather than a : T annotation.
Use .at(0) when an element may be absent; use [0] only when existence is established or documented with // SAFETY:.
Prefer arrow functions over function expressions.
Destructure function parameters when the intermediate object is not reused.
Prefer discriminated-union checks such as obj.type === "x"; use in only without an available discriminator.
Use positional parameters for one or two clear arguments; use named Options, Args, or Params objects for three or more or interchangeable arguments. Reserve Props for React props.
Reuse utility types from React,...

Files:

  • packages/stdnum/src/native-node.ts
  • packages/stdnum/src/__test__/native-node.test.ts
**/*.{ts,tsx,rs}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,rs}: Use oxlint with the Ultracite preset and oxfmt; suppress a rule only with // eslint-disable-next-line rule-name.
Use property and mutation tests for checksum behavior when the input space is large.

Files:

  • packages/stdnum/src/native-node.ts
  • packages/stdnum/src/__test__/native-node.test.ts
🔇 Additional comments (1)
packages/stdnum/src/native-node.ts (1)

72-78: LGTM!

Comment on lines +77 to +80
const binding = new Proxy(
{},
{ get: () => () => undefined },
) as NativeStdnumBinding;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document why this cast is sound.

Add a // SAFETY: comment explaining that the Proxy returns functions for every runtime contract check performed by asNativeBinding.

As per coding guidelines, “Avoid as casts; narrow with type guards… If unavoidable, add a // SAFETY: comment explaining why it is sound.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/stdnum/src/__test__/native-node.test.ts` around lines 77 - 80,
Document the unavoidable NativeStdnumBinding cast in the Proxy setup by adding a
// SAFETY: comment explaining that the Proxy returns functions for every runtime
contract check performed by asNativeBinding. Keep the existing Proxy behavior
and cast unchanged.

Source: Coding guidelines

Comment on lines +86 to +92
const reason =
match === undefined
? `This platform is not among the build targets (${supported}).`
: `This platform IS a build target, so ${match[3]} exists but was not ` +
`installed. It is an optionalDependency: check that optional ` +
`dependencies are enabled, that ${match[3]} is not excluded by an ` +
`install policy, and that it appears in your lockfile.`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not equate a target match with an uninstalled package.

match proves only that this target has a package entry. requireModule(match[3]) can also fail when the package is installed but cannot initialise (for example, a broken native binary), or when it fails the binding contract. Describe it as “could not be loaded” and leave the specific cause to Tried:.

Proposed fix
-      : `This platform IS a build target, so ${match[3]} exists but was not ` +
-        `installed. It is an optionalDependency: check that optional ` +
+      : `This platform is a build target, but ${match[3]} could not be ` +
+        `loaded. It is an optionalDependency: check that optional ` +
         `dependencies are enabled, that ${match[3]} is not excluded by an ` +
         `install policy, and that it appears in your lockfile.`;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const reason =
match === undefined
? `This platform is not among the build targets (${supported}).`
: `This platform IS a build target, so ${match[3]} exists but was not ` +
`installed. It is an optionalDependency: check that optional ` +
`dependencies are enabled, that ${match[3]} is not excluded by an ` +
`install policy, and that it appears in your lockfile.`;
const reason =
match === undefined
? `This platform is not among the build targets (${supported}).`
: `This platform is a build target, but ${match[3]} could not be ` +
`loaded. It is an optionalDependency: check that optional ` +
`dependencies are enabled, that ${match[3]} is not excluded by an ` +
`install policy, and that it appears in your lockfile.`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/stdnum/src/native-node.ts` around lines 86 - 92, Update the reason
construction in the native module loading path to describe a matched package as
one that “could not be loaded,” rather than asserting it was not installed. Keep
the unsupported-platform message for match === undefined, and leave the specific
requireModule failure cause to the existing “Tried:” details.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b6e9f9157b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +89 to +92
: `This platform IS a build target, so ${match[3]} exists but was not ` +
`installed. It is an optionalDependency: check that optional ` +
`dependencies are enabled, that ${match[3]} is not excluded by an ` +
`install policy, and that it appears in your lockfile.`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Don't report installed native packages as missing

When the platform package is installed but fails to load, for example because its binary is incompatible, a shared library is unavailable, or its exports fail asNativeBinding, match is still defined and this message falsely states that the package was not installed. The collected errors already distinguish these cases, so the missing-package diagnosis should only be used after confirming that resolving the platform package itself failed; otherwise users are directed toward optional-dependency and lockfile fixes that cannot resolve the actual load failure.

Useful? React with 👍 / 👎.

The platform binaries are optionalDependencies, so any installer that
declines them leaves this package importable and dead, throwing only on
first use. The message it threw then listed every supported target,
including the one it was running on, which reads as a portability problem
that does not exist and sends the reader looking in the wrong place.

It now separates the two cases: a platform that is not a build target,
and a platform that is one whose package was not installed. The second
names the package and the usual reasons an optionalDependency goes
missing.

The loader had no tests despite taking every input it consults as an
injectable option, which is how this stayed invisible. Both branches are
now covered.
@jan-kubica
jan-kubica force-pushed the fix/native-binding-diagnostics branch from db0757c to 046b7d0 Compare July 26, 2026 22:41

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/loud-pandas-repeat.md:
- Around line 5-9: Update the release-note wording in the changeset to replace
“missing install” with “missing installation” or “missing optional package,”
while preserving the explanation that the native binding fails because an
optional platform package was not installed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: adfd9304-12df-4cb4-b6b0-c30a0ef836b8

📥 Commits

Reviewing files that changed from the base of the PR and between b6e9f91 and 046b7d0.

📒 Files selected for processing (3)
  • .changeset/loud-pandas-repeat.md
  • packages/stdnum/src/__test__/native-node.test.ts
  • packages/stdnum/src/native-node.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/stdnum/src/test/native-node.test.ts
  • packages/stdnum/src/native-node.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Pack
  • GitHub Check: Bindings and packages
  • GitHub Check: Rust
  • GitHub Check: Oracle
🧰 Additional context used
🪛 LanguageTool
.changeset/loud-pandas-repeat.md

[grammar] ~9-~9: The word ‘install’ is not a noun.
Context: ...rtability problem rather than a missing install.

(A_INSTALL)

Comment on lines +5 to +9
Say which package is missing when the native binding cannot load. The platform
binaries are optional dependencies, so an installer that declines them leaves
the package importable and dead, throwing only on first use. The previous error
listed every supported target, including the one it was running on, which reads
as a portability problem rather than a missing install.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use clearer release-note wording.

“Missing install” is awkward here; use “missing installation” or “missing optional package” to describe the failure precisely.

🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: The word ‘install’ is not a noun.
Context: ...rtability problem rather than a missing install.

(A_INSTALL)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.changeset/loud-pandas-repeat.md around lines 5 - 9, Update the release-note
wording in the changeset to replace “missing install” with “missing
installation” or “missing optional package,” while preserving the explanation
that the native binding fails because an optional platform package was not
installed.

Source: Linters/SAST tools

@jan-kubica
jan-kubica merged commit 676724f into main Jul 26, 2026
13 checks passed
@jan-kubica
jan-kubica deleted the fix/native-binding-diagnostics branch July 26, 2026 22:54
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 26, 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.

1 participant