Skip to content

fix(server): name the missing file instead of a generic read failure - #7628

Open
DraftProducts wants to merge 2 commits into
pingdotgg:mainfrom
DraftProducts:fix/workspace-file-missing-message
Open

fix(server): name the missing file instead of a generic read failure#7628
DraftProducts wants to merge 2 commits into
pingdotgg:mainfrom
DraftProducts:fix/workspace-file-missing-message

Conversation

@DraftProducts

@DraftProducts DraftProducts commented Aug 20, 2026

Copy link
Copy Markdown

What Changed

projects.readFile now reports Workspace file 'X' does not exist in 'Y'. when
the read fails with ENOENT, instead of the generic Failed to read workspace file 'X' in 'Y'. Every other failure keeps its current message.

A missing workspace root raises ENOENT through the same call, so
realpath-workspace-root is excluded rather than blaming the file for it. The
error constructor already honored a supplied message; the failure-context type
just did not express it, which is the one line in packages/contracts. message
is already part of the schema, so nothing changes on the wire.

Tests: two cases in server.test.ts — a missing file is named, and a missing
workspace root is not relabeled as one (that second test fails without the
realpath-workspace-root guard). server.test.ts (126), workspace/ (56),
contracts (257), typecheck, lint, and format pass.

Why

The generic message reads as an internal error rather than the plain fact that
the path is not there, which sends people debugging the wrong thing.

Chat file links make this easy to hit. They are built from the text of a reply,
so any path an agent mentions becomes a clickable chip whether or not it exists
under the active workspace root, and ChatView always resolves it against that
root. The existing basename lookup in ChatMarkdown only rescues paths with no
separator, so a multi-segment path goes straight to a failed read and the user
gets the opaque message.

Not addressed: the chip is still offered for a file that is not there. Deciding
whether a link should be verified before it is rendered, or resolved across more
than one workspace root, is a product call rather than an error-message fix.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes — N/A, server-only
  • I included a video for animation/interaction changes — N/A, server-only

Changes by Claude Opus 5 running in Claude Code.


Note

Low Risk
Small, additive error classification and messaging change with integration tests; no auth or data-path changes.

Overview
projectsReadFile now classifies filesystem ENOENT (except during realpath-workspace-root) as file_not_found instead of operation_failed, and ProjectReadFileError uses an explicit message: Workspace file '…' does not exist in '…'.

Contracts add the new file_not_found failure literal. Server tests cover a missing relative path and assert a missing workspace root still surfaces operation_failed without the “does not exist” wording.

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

Note

Name the missing file in ProjectReadFileError instead of generic read failure

Adds file_not_found to the ProjectFileFailure union in project.ts and uses it to produce a message that names the relative path and cwd of the missing file.

  • projectFileFailureContext now maps workspace file read ENOENT errors to file_not_found, except when the operation is realpath-workspace-root (missing root stays operation_failed).
  • Adds WebSocket RPC tests in server.test.ts covering both missing-file and missing-root cases.
  • Risk: ProjectFileFailure gains a new literal value; out-of-tree consumers that exhaustively match the union need a case for file_not_found.
📊 Macroscope summarized 46ccbf9. 2 files reviewed, 1 issue evaluated, 1 issue filtered, 0 comments posted

🗂️ Filtered Issues

apps/server/src/ws.ts — 0 comments posted, 1 evaluated, 1 filtered
  • line 245: projectFileFailureContext is shared by both projectsReadFile and projectsWriteFile, so this classifies any write-side ENOENT from make-directory or write-file as file_not_found. A write target is allowed to be absent (the operation creates it); such an ENOENT instead indicates a failed parent-directory/write operation, for example if a parent is concurrently removed. ProjectWriteFileError.failure therefore changes from operation_failed to the misleading file_not_found. Restrict this classification to the read RPC/read operations, or use separate failure mapping for writes. [ Already posted ]

Opening a file that is not in the thread's workspace reports:

    Failed to read workspace file 'components/selectors/Foo.vue' in '/repo'.

which reads as an internal error rather than the plain fact that the path is
not there. The server already knows: the read fails with ENOENT and the
structured `failure`/`operation` fields are carried on the error. Only the
message was generic.

Chat file links make this easy to hit. They are built from the text of a
reply, so any path an agent mentions becomes a clickable chip whether or not it
exists under the active workspace root, and `ChatView` always resolves it
against that root. The existing basename lookup only rescues paths with no
separator, so a multi-segment path goes straight to a failed read.

A missing workspace root raises ENOENT through the same call, so
`realpath-workspace-root` is excluded rather than blaming the file for it. Every
other failure keeps its current message; `message` was already honored by the
error constructor and is now expressible on the failure-context type.
@coderabbitai

coderabbitai Bot commented Aug 20, 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 Plus

Run ID: e9eff39d-65bf-4b00-8a3f-82181cf4ad91

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

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.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Aug 20, 2026

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One convention issue: the new "file does not exist" wording is passed to ProjectReadFileError as a prebuilt message string instead of being modeled as a structural attribute and derived inside the error class.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/ws.ts Outdated
Comment thread packages/contracts/src/project.ts Outdated
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 20, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 46ccbf9

Macroscope's review found this PR approvable — This PR improves error messaging for missing workspace files by detecting ENOENT errors and providing specific error messages. Changes are minimal, additive to the schema, and well-tested with comprehensive test coverage for both the main case and edge cases.

You can add or adjust custom eligibility rules. Learn more.

Review feedback: the not-found wording was handed to ProjectReadFileError as a
prebuilt `message`, so the distinction lived in text while `failure` stayed
`"operation_failed"`. The `decodedProjectErrorMessage` path exists to decode
legacy message-only payloads, not to override messages from new code.

Classify instead: `projectFileFailureContext` returns a new `"file_not_found"`
member of `ProjectFileFailure`, and `ProjectReadFileError` derives the message
from `failure`, `relativePath`, and `cwd`. `message` is gone from
`ProjectFileFailureContext`, so callers can no longer inject one.

The guard is unchanged in substance: a missing workspace root raises ENOENT
through the same operation, so `realpath-workspace-root` still classifies as
`operation_failed`. Nothing reads these literals for control flow today.
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 20, 2026 03:03

Dismissing prior approval to re-evaluate 46ccbf9

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

Labels

size:S 10-29 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.

1 participant