fix: reject missing files before opening editor tabs#9012
Conversation
📝 WalkthroughWalkthrough
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: e309d51f-4e8e-4a8e-8147-d1395f814765
📒 Files selected for processing (3)
src/cli/handlers/file.test.tssrc/main/runtime/orca-runtime-files.test.tssrc/main/runtime/orca-runtime-files.ts
| it('reports a missing file as a JSON failure with a nonzero exit code', async () => { | ||
| const priorExitCode = process.exitCode | ||
| queueFixtures(callMock, worktreeListFixture([buildWorktree('/tmp/repo', 'feature')])) | ||
| callMock.mockRejectedValueOnce( | ||
| new RuntimeRpcFailureError({ | ||
| id: 'req_open', | ||
| ok: false, | ||
| error: { code: 'runtime_error', message: 'File not found: docs/missing.md' }, | ||
| _meta: { runtimeId: 'runtime-1' } | ||
| }) | ||
| ) | ||
|
|
||
| await main(['file', 'open', 'docs/missing.md', '--json'], '/tmp/repo') | ||
|
|
||
| expect(JSON.parse(String(vi.mocked(console.log).mock.calls[0][0]))).toMatchObject({ | ||
| ok: false, | ||
| error: { code: 'runtime_error', message: 'File not found: docs/missing.md' } | ||
| }) | ||
| expect(process.exitCode).toBe(1) | ||
|
|
||
| process.exitCode = priorExitCode | ||
| }) | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use a try...finally block to ensure process.exitCode is restored.
If an assertion (like expect) fails during the test, it throws an error and halts the test execution. Without a try...finally block, process.exitCode will not be restored to priorExitCode, which can cause subsequent tests to misbehave or the test runner process to exit with an incorrect status code.
🛡️ Proposed fix to ensure cleanup
it('reports a missing file as a JSON failure with a nonzero exit code', async () => {
const priorExitCode = process.exitCode
- queueFixtures(callMock, worktreeListFixture([buildWorktree('/tmp/repo', 'feature')]))
- callMock.mockRejectedValueOnce(
- new RuntimeRpcFailureError({
- id: 'req_open',
- ok: false,
- error: { code: 'runtime_error', message: 'File not found: docs/missing.md' },
- _meta: { runtimeId: 'runtime-1' }
- })
- )
-
- await main(['file', 'open', 'docs/missing.md', '--json'], '/tmp/repo')
-
- expect(JSON.parse(String(vi.mocked(console.log).mock.calls[0][0]))).toMatchObject({
- ok: false,
- error: { code: 'runtime_error', message: 'File not found: docs/missing.md' }
- })
- expect(process.exitCode).toBe(1)
-
- process.exitCode = priorExitCode
+ try {
+ queueFixtures(callMock, worktreeListFixture([buildWorktree('/tmp/repo', 'feature')]))
+ callMock.mockRejectedValueOnce(
+ new RuntimeRpcFailureError({
+ id: 'req_open',
+ ok: false,
+ error: { code: 'runtime_error', message: 'File not found: docs/missing.md' },
+ _meta: { runtimeId: 'runtime-1' }
+ })
+ )
+
+ await main(['file', 'open', 'docs/missing.md', '--json'], '/tmp/repo')
+
+ expect(JSON.parse(String(vi.mocked(console.log).mock.calls[0][0]))).toMatchObject({
+ ok: false,
+ error: { code: 'runtime_error', message: 'File not found: docs/missing.md' }
+ })
+ expect(process.exitCode).toBe(1)
+ } finally {
+ process.exitCode = priorExitCode
+ }
})📝 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.
| it('reports a missing file as a JSON failure with a nonzero exit code', async () => { | |
| const priorExitCode = process.exitCode | |
| queueFixtures(callMock, worktreeListFixture([buildWorktree('/tmp/repo', 'feature')])) | |
| callMock.mockRejectedValueOnce( | |
| new RuntimeRpcFailureError({ | |
| id: 'req_open', | |
| ok: false, | |
| error: { code: 'runtime_error', message: 'File not found: docs/missing.md' }, | |
| _meta: { runtimeId: 'runtime-1' } | |
| }) | |
| ) | |
| await main(['file', 'open', 'docs/missing.md', '--json'], '/tmp/repo') | |
| expect(JSON.parse(String(vi.mocked(console.log).mock.calls[0][0]))).toMatchObject({ | |
| ok: false, | |
| error: { code: 'runtime_error', message: 'File not found: docs/missing.md' } | |
| }) | |
| expect(process.exitCode).toBe(1) | |
| process.exitCode = priorExitCode | |
| }) | |
| it('reports a missing file as a JSON failure with a nonzero exit code', async () => { | |
| const priorExitCode = process.exitCode | |
| try { | |
| queueFixtures(callMock, worktreeListFixture([buildWorktree('/tmp/repo', 'feature')])) | |
| callMock.mockRejectedValueOnce( | |
| new RuntimeRpcFailureError({ | |
| id: 'req_open', | |
| ok: false, | |
| error: { code: 'runtime_error', message: 'File not found: docs/missing.md' }, | |
| _meta: { runtimeId: 'runtime-1' } | |
| }) | |
| ) | |
| await main(['file', 'open', 'docs/missing.md', '--json'], '/tmp/repo') | |
| expect(JSON.parse(String(vi.mocked(console.log).mock.calls[0][0]))).toMatchObject({ | |
| ok: false, | |
| error: { code: 'runtime_error', message: 'File not found: docs/missing.md' } | |
| }) | |
| expect(process.exitCode).toBe(1) | |
| } finally { | |
| process.exitCode = priorExitCode | |
| } | |
| }) |
Closes #8844
Summary
Prevent
orca file openfrom reporting success for paths that do notexist.
The runtime now verifies the target file before opening an editor tab:
ok: falseand cause the CLI to exit with code1, instead of creating a ghost editor tab.Screenshots
No visual change.
Testing
pnpm lint(targetedoxlintand formatting checks passed)pnpm typecheck(runtime and CLI type checks passed)pnpm test(full Vitest suite run on Node 24: 29,938 passed, 0failed)
pnpm buildand CLI JSON failure / nonzero exit code
AI Review Report
Reviewed the file-open flow from CLI handler to RPC dispatcher to runtime
file commands.
Main risks checked:
The review found that local and SSH paths required different existence
checks. The implementation now uses
resolveAuthorizedPath+statlocally and the SSH filesystem provider remotely.
Cross-platform compatibility was reviewed for macOS, Linux, and Windows.
The change uses existing Node path utilities and filesystem abstractions,
does not add shell commands, keyboard shortcuts, labels, or Electron-
specific behavior, and preserves the existing local/SSH routing model.
Security Audit
Reviewed path handling and IPC/RPC behavior.
validation.
resolveAuthorizedPathbefore filesystemaccess.
instead of local disk access.
absolute filesystem path.
new IPC surface was added.
No follow-up security work is required for this change.
Notes
requirement.