Skip to content

fix(file-explorer): open symlink files when stat fails - #11670

Merged
brennanb2025 merged 2 commits into
stablyai:mainfrom
xianjianlf2:fix/symlink-open-11654
Aug 10, 2026
Merged

fix(file-explorer): open symlink files when stat fails#11670
brennanb2025 merged 2 commits into
stablyai:mainfrom
xianjianlf2:fix/symlink-open-11654

Conversation

@xianjianlf2

Copy link
Copy Markdown
Contributor

Summary

  • allow file symlink activation to continue to the editor when target stat fails
  • keep symlink-to-directory expansion unchanged when stat succeeds
  • add a regression test for stat failures on symlink file activation

Fixes #11654.

Tests

  • pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/right-sidebar/useFileExplorerHandlers.test.ts
  • pnpm exec oxlint src/renderer/src/components/right-sidebar/useFileExplorerHandlers.ts src/renderer/src/components/right-sidebar/useFileExplorerHandlers.test.ts
  • git diff --check

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 750c11f3-ea08-4151-8d34-15fa7d3ab2c7

📥 Commits

Reviewing files that changed from the base of the PR and between 58255bc and ed98ebb.

📒 Files selected for processing (5)
  • src/renderer/src/components/right-sidebar/FileExplorer.tsx
  • src/renderer/src/components/right-sidebar/file-explorer-deferred-dir-toggle.test.ts
  • src/renderer/src/components/right-sidebar/file-explorer-drag-scroll-marker.test.tsx
  • src/renderer/src/components/right-sidebar/useFileExplorerHandlers.test.ts
  • src/renderer/src/components/right-sidebar/useFileExplorerHandlers.ts

📝 Walkthrough

Walkthrough

File explorer handlers now receive an external-path authorization callback. Local symlink activation authorizes the target before stat resolution. Remote-owned symlinks skip local authorization. Authorization and stat failures open the target as a preview file. Tests cover these cases and update handler setup dependencies.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description includes a summary and targeted tests but omits Screenshots, AI Review Report, Security Audit, Notes, and required checklist status. Add all template sections, state “No visual change” if applicable, complete the testing checklist, and document AI review and security findings.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the primary fix for opening symlink files when target stat fails.
Linked Issues check ✅ Passed The implementation addresses issue #11654 by opening symlink files when target stat fails and preserves directory expansion behavior.
Out of Scope Changes check ✅ Passed The authorization wiring and regression tests directly support symlink activation and do not introduce unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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.

@greptile-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown

Greptile Summary

When statPath threw on a symlink (e.g. TCC-protected targets on macOS), the previous code showed a toast error and returned early, blocking the user from opening the file at all. This PR replaces that early-return with targetIsDirectory = false, letting the normal file-open path run instead.

  • useFileExplorerHandlers.ts: The catch block in the symlink branch is reduced from a toast + return to a single targetIsDirectory = false assignment; the directory-loading failure toast (when loadDir returns false) is preserved.
  • useFileExplorerHandlers.test.ts: A new regression test mirrors the existing non-directory symlink test but passes a rejecting statPath mock to pin the corrected behaviour.

Confidence Score: 5/5

Safe to merge — the fix is a one-line change in a single branch of the symlink-activation flow, the rest of the logic is untouched, and the new test directly covers the corrected path.

The change is minimal and well-scoped: only the catch block inside the isSymlink branch is modified. The symlink-to-directory path (stat succeeds with isDirectory: true) is unchanged. The fallthrough to the shared openFile call already existed and is exercised by other tests. The new test provides direct regression coverage of the failure case, and the implementation matches what the test asserts.

Files Needing Attention: No files require special attention.

Important Files Changed

Filename Overview
src/renderer/src/components/right-sidebar/useFileExplorerHandlers.ts Removes the early-return toast error on statPath failure for symlinks; instead defaults targetIsDirectory to false and falls through to open the node as a file. The rest of the symlink/directory/file logic is untouched.
src/renderer/src/components/right-sidebar/useFileExplorerHandlers.test.ts Adds a regression test covering the stat-failure path: verifies that openFile is called with the correct params when statPath rejects, mirroring the existing non-directory symlink test.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[activateFileExplorerNode] --> B{activeWorktreeId?}
    B -->|null| Z[return]
    B -->|set| C[setSelectedPath]
    C --> D{node.isDirectory?}
    D -->|yes| E{canToggleDirectories?}
    E -->|no| Z
    E -->|yes| F[toggleDir]
    D -->|no| G{node.isSymlink?}
    G -->|no| M[requireMatchingRoute]
    G -->|yes| H[statPath node.path]
    H -->|success| I{isDirectory?}
    H -->|error - BEFORE: toast + return / AFTER: targetIsDirectory = false| I
    I -->|true| J[loadDir with force+failOnError]
    J -->|loaded| K[markPathAsDirectory + toggleDir]
    J -->|failed| L[toast.error Cannot open symlink target]
    I -->|false| M
    M -->|route found| N[openFile as edit preview]
    M -->|route error| O[toast.error owner unresolved]
Loading

Reviews (1): Last reviewed commit: "fix(file-explorer): open symlink files w..." | Re-trigger Greptile

Following a symlink out of the workspace was denied by the main-process
path allow-list, so both the stat and the file read failed. Activating the
row is explicit intent, so authorize the target the way terminal links and
Quick Open already do.
@brennanb2025

Copy link
Copy Markdown
Contributor

Thanks for chasing this down — the diagnosis is right. I've pushed a follow-up commit to your branch (heads up so it isn't a surprise).

Testing it in the app turned up a second half to the bug. The reason statPath rejects is that the main process's path allow-list refuses any symlink whose real target resolves outside the workspace — and fs:readFile goes through that same check. So with only the toast removed, clicking a symlink that points outside the repo swapped the error message for an editor tab reading "Access denied: path resolves outside allowed directories".

What the follow-up adds:

  • On activation of a symlink row in a local workspace, grant that one path access first — the same thing that already happens when you click a file path in a terminal or open an absolute path from Quick Open. With that in place the stat and the read succeed and the file opens. It also fixes symlinks pointing at a directory outside the workspace, which hit the same error.
  • Skipped for SSH and remote-runtime workspaces, where the host owns that boundary.
  • The grant sits inside the existing try, so if it ever fails the click still falls through to the editor's real error instead of dead-ending silently.
  • Tests: the grant must land before the stat, remote-owned workspaces must not get a local grant, and a failed grant must still open the file. Two other test files that call the hook needed the new argument to keep the typecheck green.

Worth knowing: #11681 and #4556 are also open against #11654 and take a different route — relaxing the allow-list itself rather than granting per activation. A maintainer will decide which approach lands.

Checked on macOS against a symlink to a file outside the repo, a symlink to a directory outside the repo, a symlink inside the repo, and a broken symlink (that one now shows a plain "file not found" in the tab).

@brennanb2025
brennanb2025 merged commit 80f23e3 into stablyai:main Aug 10, 2026
45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Symlink file cannot be opened, showing "Cannot open symlink target"

3 participants