Skip to content

fix(electron): Fix broken symlinks in server bundle preventing app startup#743

Merged
Shironex merged 2 commits intov0.14.0rcfrom
fix/broken-syslinks-on-server
Feb 2, 2026
Merged

fix(electron): Fix broken symlinks in server bundle preventing app startup#743
Shironex merged 2 commits intov0.14.0rcfrom
fix/broken-syslinks-on-server

Conversation

@Shironex
Copy link
Collaborator

@Shironex Shironex commented Feb 2, 2026

Fixes #742

This commit resolves two critical issues that prevented the Electron app from starting:

  1. Broken symlinks in server bundle

    • After npm install, local @automaker/* packages were symlinked in node_modules
    • These symlinks broke after electron-builder packaging since relative paths no longer existed
    • Solution: Added Step 6b in prepare-server.mjs to replace symlinks with real directory copies
    • Added lstatSync and resolve imports to support symlink detection and replacement
  2. electronUserDataWriteFileSync fails on first launch

    • The userData directory doesn't exist on first app launch
    • Writing .api-key file would fail with ENOENT error
    • Solution: Added directory existence check and creation with { recursive: true } before writing

Files modified:

  • apps/ui/scripts/prepare-server.mjs: Added symlink replacement logic after npm install
  • libs/platform/src/system-paths.ts: Added parent directory creation in electronUserDataWriteFileSync

Verification: After these fixes, npm run build:electron produces a working app that starts without ERR_MODULE_NOT_FOUND errors.

Summary by CodeRabbit

  • Bug Fixes
    • Replaced symlink-based package references with actual directory copies during installation, improving compatibility and stability of packaged deployments.
    • Ensured parent directories are created automatically before writing files, preventing write failures on first-run and improving startup reliability.

…artup

Fixes #742

This commit resolves two critical issues that prevented the Electron app from starting:

1. **Broken symlinks in server bundle**
   - After npm install, local @automaker/* packages were symlinked in node_modules
   - These symlinks broke after electron-builder packaging since relative paths no longer existed
   - Solution: Added Step 6b in prepare-server.mjs to replace symlinks with real directory copies
   - Added lstatSync and resolve imports to support symlink detection and replacement

2. **electronUserDataWriteFileSync fails on first launch**
   - The userData directory doesn't exist on first app launch
   - Writing .api-key file would fail with ENOENT error
   - Solution: Added directory existence check and creation with { recursive: true } before writing

Files modified:
- apps/ui/scripts/prepare-server.mjs: Added symlink replacement logic after npm install
- libs/platform/src/system-paths.ts: Added parent directory creation in electronUserDataWriteFileSync

Verification: After these fixes, npm run build:electron produces a working app that starts without ERR_MODULE_NOT_FOUND errors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@Shironex Shironex self-assigned this Feb 2, 2026
@Shironex Shironex added Bug Something isn't working Testers-Requested Request for others to test an enhancement or bug fix/etc. Do Not Merge Use this label if something should not be merged. labels Feb 2, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Shironex, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses two critical issues that were preventing the Electron application from starting. It resolves problems related to broken symbolic links for local packages within the packaged server bundle and ensures that user data files can be written successfully on the very first launch, even when the application's dedicated data directory has not yet been created.

Highlights

  • Fix broken symlinks in Electron server bundle: Local @automaker/* packages, initially symlinked by npm install, would break during electron-builder packaging due to relative path issues. This PR introduces a new step in prepare-server.mjs to detect these symlinks and replace them with actual directory copies, ensuring the packaged application starts correctly.
  • Prevent electronUserDataWriteFileSync failure on first launch: The userData directory might not exist on the Electron app's initial launch, leading to ENOENT errors when attempting to write files like .api-key. The electronUserDataWriteFileSync function is now updated to check for and recursively create the necessary parent directories before performing the file write operation.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 2, 2026

📝 Walkthrough

Walkthrough

This PR replaces symlinked local packages in the server bundle with real directory copies after npm install, and ensures electronUserData write calls create parent directories before writing files.

Changes

Cohort / File(s) Summary
Build script — symlink replacement
apps/ui/scripts/prepare-server.mjs
After npm install, detect symlinks under node_modules/@automaker/* via lstatSync, resolve real package paths with resolve, remove symlinks and copy real directories into the bundle. Added ENOENT-safe try/catch and new imports (lstatSync, resolve).
Filesystem utilities — ensure parent dir
libs/platform/src/system-paths.ts
electronUserDataWriteFileSync now computes the full path, ensures the parent directory exists (creates it recursively if missing) before calling writeFileSync. No signature changes.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I hopped through node_modules, sniffed each link and trail,
Replaced the fragile threads with stomping-copy tail.
I dug a cozy folder where userData may hide,
Now first launch finds its keys — I twitch with quiet pride. 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(electron): Fix broken symlinks in server bundle preventing app startup' directly and clearly summarizes the main change—resolving broken symlinks that prevent app startup.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from issue #742: replacing broken symlinks with real directory copies in prepare-server.mjs and ensuring parent directory creation in electronUserDataWriteFileSync.
Out of Scope Changes check ✅ Passed All changes in both modified files directly address the linked issue requirements with no extraneous modifications or out-of-scope additions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/broken-syslinks-on-server

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.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces two important fixes for the Electron app startup. The first fix addresses broken symlinks in the server bundle by replacing them with actual directory copies, which is a solid approach. The second fix ensures that the userData directory is created on first launch, preventing file write errors.

I've identified a potential issue in the symlink replacement logic where broken symlinks might not be handled correctly. I've also suggested a minor simplification in the directory creation logic. Overall, these are great fixes that improve the robustness of the application startup.

- Use lstatSync with try/catch for robust broken symlink detection
- Remove redundant existsSync check before mkdirSync with recursive: true

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/platform/src/system-paths.ts (1)

744-758: ⚠️ Potential issue | 🟠 Major

Add path traversal protection to electronUserDataWriteFileSync.

The function currently accepts relativePath without validation, allowing .. sequences to escape electronUserDataPath. While existing call sites use hardcoded filenames, the API should enforce containment to prevent future misuse. Add normalized path validation before directory creation and write.

🔒 Proposed fix
-  const fullPath = path.join(electronUserDataPath, relativePath);
-  // Ensure parent directory exists (may not exist on first launch)
-  const dir = path.dirname(fullPath);
-  if (!fsSync.existsSync(dir)) {
-    fsSync.mkdirSync(dir, { recursive: true });
-  }
-  fsSync.writeFileSync(fullPath, data, options);
+  const fullPath = path.resolve(electronUserDataPath, relativePath);
+  const normalizedUserData = path.resolve(electronUserDataPath);
+  if (
+    fullPath !== normalizedUserData &&
+    !fullPath.startsWith(normalizedUserData + path.sep)
+  ) {
+    throw new Error('[SystemPaths] Electron userData path traversal blocked');
+  }
+  const dir = path.dirname(fullPath);
+  fsSync.mkdirSync(dir, { recursive: true });
+  fsSync.writeFileSync(fullPath, data, options);
🧹 Nitpick comments (1)
apps/ui/scripts/prepare-server.mjs (1)

115-128: Harden symlink replacement against TOCTOU races and Windows junctions.

The existing existsSync + lstatSync check before rmSync creates a race condition: the path state can change between the checks and the removal. On Windows, npm can create junctions in addition to symlinks. Wrap the logic in a try/catch block and use rmSync with { recursive: true, force: true } for robustness; also validate that the real package directory exists before copying to avoid leaving a missing target.

♻️ Proposed change
 const nodeModulesAutomaker = join(BUNDLE_DIR, 'node_modules', '@automaker');
 for (const pkgName of LOCAL_PACKAGES) {
   const pkgDir = pkgName.replace('@automaker/', '');
   const nmPkgPath = join(nodeModulesAutomaker, pkgDir);
-  if (existsSync(nmPkgPath) && lstatSync(nmPkgPath).isSymbolicLink()) {
-    const realPath = resolve(BUNDLE_DIR, 'libs', pkgDir);
-    rmSync(nmPkgPath);
-    cpSync(realPath, nmPkgPath, { recursive: true });
-    console.log(`   ✓ Replaced symlink: ${pkgName}`);
-  }
+  try {
+    if (!lstatSync(nmPkgPath).isSymbolicLink()) continue;
+    const realPath = resolve(BUNDLE_DIR, 'libs', pkgDir);
+    if (!existsSync(realPath)) {
+      console.warn(`⚠️  Missing real package dir for ${pkgName}: ${realPath}`);
+      continue;
+    }
+    rmSync(nmPkgPath, { recursive: true, force: true });
+    cpSync(realPath, nmPkgPath, { recursive: true });
+    console.log(`   ✓ Replaced symlink: ${pkgName}`);
+  } catch {
+    // Not a symlink or disappeared between checks; skip.
+  }
 }

@Shironex Shironex merged commit 00e4712 into v0.14.0rc Feb 2, 2026
6 of 7 checks passed
@Shironex Shironex deleted the fix/broken-syslinks-on-server branch February 2, 2026 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working Do Not Merge Use this label if something should not be merged. Testers-Requested Request for others to test an enhancement or bug fix/etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant