Skip to content

fix(desktop): stage ffi-rs native bindings so the packaged app launches a window#3129

Open
akj wants to merge 1 commit into
pingdotgg:mainfrom
akj:fix/desktop-stage-ffi-rs-native-bindings
Open

fix(desktop): stage ffi-rs native bindings so the packaged app launches a window#3129
akj wants to merge 1 commit into
pingdotgg:mainfrom
akj:fix/desktop-stage-ffi-rs-native-bindings

Conversation

@akj

@akj akj commented Jun 17, 2026

Copy link
Copy Markdown

Problem

The packaged desktop app launches a main process but never opens a window. The bundled backend server crash-loops on startup with:

Error: Cannot find module '@yuuang/ffi-rs-win32-x64-msvc'
Require stack:
- <install>\resources\app.asar\node_modules\ffi-rs\index.js

Because the desktop window is gated on backend HTTP readiness (DesktopBackendManager polls /.well-known/t3/environment, then DesktopWindow.handleBackendReady creates the window), a backend that never becomes ready means the window is never created — while the main process stays alive restarting the backend forever. First observed on nightly 0.0.28-nightly.20260616.571 (win32-x64).

Root cause

@ff-labs/fff-node (the fff workspace-search integration, #3099) loads its native binary through the ffi-rs FFI runtime. ffi-rs@1.3.2 loads its prebuilt addon from a platform-specific package — on Windows x64, @yuuang/ffi-rs-win32-x64-msvc (an optionalDependency of ffi-rs).

#3109 staged the fff binary packages (@ff-labs/fff-bin-*) and added them to asarUnpack, but did not stage ffi-rs's own native binding. electron-builder reliably keeps direct production deps but prunes platform-gated transitive optional deps in pnpm layouts, so @yuuang/ffi-rs-* was dropped from the package entirely. The staging logic omitted it for all platforms, so mac/linux builds are affected too.

Fix

Mirror the @ff-labs/fff-bin-* treatment for the FFI binding in scripts/build-desktop-artifact.ts:

  1. resolveFfiRsNativeDependencies(platform, arch, version) — promote the target-triple @yuuang/ffi-rs-* binding to a direct staged dependency so electron-builder keeps it (covers mac darwin-{arm64,x64}, win win32-{x64,arm64}-msvc, linux linux-{x64,arm64}-{gnu,musl}, and universal).
  2. Add node_modules/@yuuang/ffi-rs-*/**/* to DESKTOP_ASAR_UNPACK so the .node loads outside the asar.
  3. The version is read from pnpm-lock.yaml (ffi-rs is transitive, so it has no package.json entry; bindings ship in lockstep with ffi-rs). The build fails loudly if ffi-rs can't be resolved, so this can't silently regress.

Validation

  • scripts typecheck clean; new unit tests for resolveFfiRsNativeDependencies and parseFfiRsLockfileVersion; full scripts suite green.
  • Built a win/x64 artifact and confirmed resources/app.asar.unpacked/node_modules/@yuuang/ffi-rs-win32-x64-msvc/ffi-rs.win32-x64-msvc.node is now present (the broken nightly had no @yuuang dir at all).
  • Launched the packaged build: backend starts once with no crash loop and no "Cannot find module" error, and a real window opens (renderer process + top-level window confirmed).

Follow-up (not in this PR)

The build now enumerates two native package families by hand (@ff-labs/fff-bin-*, @yuuang/ffi-rs-*). A deeper fix would auto-discover .node-bearing packages in the staged tree and unpack them, so a future native transitive dep can't reintroduce this bug class. A win32-x64 smoke test that boots the backend would also have caught this.

Fixes #3125


Note

Medium Risk
Changes the desktop staging/install path that determines which native modules ship in production artifacts; mistakes could break all platform builds, but the change mirrors an existing pattern and adds explicit lockfile validation and tests.

Overview
Fixes packaged desktop builds where the bundled backend crash-looped on missing @yuuang/ffi-rs-* native modules (used by @ff-labs/fff-node via ffi-rs), so the window never opened.

The desktop artifact script now promotes platform-specific @yuuang/ffi-rs-* packages into staged production dependencies (same approach as @ff-labs/fff-bin-*), unpacks them from the asar via DESKTOP_ASAR_UNPACK, and pins binding versions by parsing the resolved ffi-rs version from pnpm-lock.yaml (with a hard failure if it's missing or ambiguous). Unit tests cover dependency resolution, lockfile parsing, and unpack globs.

Reviewed by Cursor Bugbot for commit 7ca71ab. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Stage ffi-rs native bindings so the packaged Electron app launches a window

  • Adds @yuuang/ffi-rs-* packages to DESKTOP_ASAR_UNPACK so Electron unpacks and can load the native addons at runtime.
  • Adds resolveFfiRsNativeDependencies to compute the platform/arch-specific @yuuang/ffi-rs-* packages to stage, including both gnu and musl variants on Linux.
  • Adds parseFfiRsLockfileVersion to extract the single resolved ffi-rs version from pnpm-lock.yaml, throwing if the version is absent or inconsistent.
  • The buildDesktopArtifact handler now reads pnpm-lock.yaml to determine the ffi-rs version and merges the resulting native packages into the staged install.
  • Risk: the build now fails with a BuildScriptError if pnpm-lock.yaml is unreadable or ffi-rs is missing or resolved to multiple versions.

Macroscope summarized 7ca71ab.


Open in Devin Review

The packaged backend crash-loops on startup with "Cannot find module
'@yuuang/ffi-rs-win32-x64-msvc'". ffi-rs (used by @ff-labs/fff-node)
loads its prebuilt native addon from a platform-specific @yuuang/ffi-rs-*
package, which was never staged into the desktop build. Because the
desktop window is gated on backend HTTP readiness, the backend never
becomes ready and no window ever opens.

Mirror the existing @ff-labs/fff-bin-* staging: promote the target-triple
@yuuang/ffi-rs-* binding to a direct staged dependency (version resolved
from pnpm-lock.yaml, where ffi-rs is pinned) and add it to asarUnpack so
the .node loads outside the asar archive.

Refs pingdotgg#3125
@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7340e2f7-4b42-4fa5-ade7-c2d2b6396691

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Jun 17, 2026
@macroscopeapp

macroscopeapp Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved

Build script fix that ensures ffi-rs native bindings are properly staged for the packaged desktop app. The new functions mirror existing patterns exactly, are purely build-time changes, and include comprehensive unit tests.

You can customize Macroscope's approvability policy. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Nightly desktop app never opens a window — backend crash-loops on missing @yuuang/ffi-rs native binding

1 participant