Skip to content

fix: distinguish outside-workspace errors from not-found in fs-safe#29715

Merged
obviyus merged 4 commits intoopenclaw:mainfrom
YuzuruS:fix/outside-workspace-error-message
Feb 28, 2026
Merged

fix: distinguish outside-workspace errors from not-found in fs-safe#29715
obviyus merged 4 commits intoopenclaw:mainfrom
YuzuruS:fix/outside-workspace-error-message

Conversation

@YuzuruS
Copy link
Contributor

@YuzuruS YuzuruS commented Feb 28, 2026

Summary

Describe the problem and fix in 2–5 bullets:

  • Problem: When the Edit tool tries to open a file outside the workspace root, SafeOpenError throws with code "invalid-path" and message "path escapes root". Consumers cannot distinguish this from other invalid-path cases (hardlinks, symlinks, non-files) and often fall back to a misleading "File not found" message.
  • Why it matters: Users see "File not found" when the real cause is "file is outside workspace", making debugging confusing.
  • What changed: Added a new "outside-workspace" error code to SafeOpenErrorCode. All path-escapes-root checks in openFileWithinRoot / writeFileWithinRoot now use this code. Consumers (pi-tools.read.ts, browser/paths.ts, media/server.ts) handle it with specific, accurate messages.
  • What did NOT change (scope boundary): Other "invalid-path" uses (hardlinks, symlinks, non-files) remain unchanged. readLocalFileSafely is unaffected (no root boundary check).

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • Edit/Write tools now report "file is outside workspace root" or "File is outside {scopeLabel}" instead of "File not found" or "Invalid path" when targeting a file outside the workspace.
  • Media server returns 400 "file is outside workspace root" instead of 400 "invalid path" for outside-workspace requests.
  • Sandbox FS errors for outside-workspace paths report EACCES instead of rethrowing a raw SafeOpenError.

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: macOS Darwin 24.0.0
  • Runtime/container: Node.js via pnpm
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): N/A

Steps

  1. Create a workspace directory and a file outside it
  2. Call openFileWithinRoot with a relative path that escapes the root (e.g. ../outside/file.txt)
  3. Observe the error code and message

Expected

  • SafeOpenError with code "outside-workspace" and message "file is outside workspace root"

Actual

  • Previously: SafeOpenError with code "invalid-path" and message "path escapes root"

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

pnpm vitest run src/infra/fs-safe.test.ts — all 11 tests pass, including updated traversal expectations ("outside-workspace").

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: pnpm build, pnpm tsgo, pnpm lint, pnpm vitest run src/infra/fs-safe.test.ts all pass
  • Edge cases checked: read traversal, write traversal, symlink escape (via existing tests)
  • What you did not verify: Windows CI (no Windows environment available locally), end-to-end Edit tool UX in the CLI

Compatibility / Migration

  • Backward compatible? No — code that matches err.code === "invalid-path" for path-escapes-root will no longer trigger; they need to also check "outside-workspace".
  • Config/env changes? No
  • Migration needed? No — consumers in this repo are updated in this PR. External consumers of the plugin-sdk SafeOpenErrorCode type will get a compile error if they have exhaustive switches, which is the desired behavior.

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Revert this commit; change "outside-workspace" back to "invalid-path" with "path escapes root" message
  • Files/config to restore: src/infra/fs-safe.ts, src/agents/pi-tools.read.ts, src/browser/paths.ts, src/media/server.ts, src/infra/fs-safe.test.ts
  • Known bad symptoms reviewers should watch for: Any consumer that matches only "invalid-path" for workspace boundary errors would silently miss the new code and fall through to a generic handler

Risks and Mitigations

  • Risk: External plugin-sdk consumers with exhaustive SafeOpenErrorCode switches get a compile error
    • Mitigation: This is intentional — the new type variant forces them to handle the case explicitly. The .d.ts is auto-generated from source.

@openclaw-barnacle openclaw-barnacle bot added agents Agent runtime and tooling size: XS labels Feb 28, 2026
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 28, 2026

Greptile Summary

Added new "outside-workspace" error code to SafeOpenErrorCode to distinguish path-escapes-root errors from generic invalid-path errors. Previously, these cases used "invalid-path" leading to misleading "File not found" messages in some consumers.

Key changes:

  • Added "outside-workspace" to the error code union type in fs-safe.ts
  • Updated 5 locations in fs-safe.ts where path-escapes-root checks occur (both in openFileWithinRoot and writeFileWithinRoot)
  • Updated consumers to handle the new error code with specific messages:
    • pi-tools.read.ts: Maps to EACCES error
    • paths.ts: Returns "File is outside {scopeLabel}" message
    • server.ts: Returns 400 with "file is outside workspace root"
  • Updated test expectations to verify "outside-workspace" error code

Additional files with SafeOpenError handling (not updated, use generic error handling):

  • src/web/media.ts: Falls through to generic "Local media path is not safe to read"
  • src/gateway/server-methods/agents.ts: Falls through to "unsafe workspace file" (appropriate for security errors)
  • src/media/store.ts: Has comprehensive error handling but maps "outside-workspace" to generic "invalid-path" via default case (see comment)

Confidence Score: 5/5

  • Safe to merge - clean refactoring that improves error specificity with minimal risk
  • All changes are backwards compatible (existing "invalid-path" handlers still work), new error code is used consistently across core functions, tests are comprehensive and passing, and all updated consumers handle the new code appropriately
  • No files require special attention - optional improvement suggested in src/media/store.ts for consistency

Last reviewed commit: a6f9988

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

5 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 28, 2026

Additional Comments (1)

src/media/store.ts
add case for "outside-workspace" to provide more specific error message

function toSaveMediaSourceError(err: SafeOpenError): SaveMediaSourceError {
  switch (err.code) {
    case "symlink":
      return new SaveMediaSourceError("invalid-path", "Media path must not be a symlink", {
        cause: err,
      });
    case "not-file":
      return new SaveMediaSourceError("not-file", "Media path is not a file", { cause: err });
    case "path-mismatch":
      return new SaveMediaSourceError("path-mismatch", "Media path changed during read", {
        cause: err,
      });
    case "too-large":
      return new SaveMediaSourceError("too-large", "Media exceeds 5MB limit", { cause: err });
    case "not-found":
      return new SaveMediaSourceError("not-found", "Media path does not exist", { cause: err });
    case "outside-workspace":
      return new SaveMediaSourceError("invalid-path", "Media path is outside workspace root", {
        cause: err,
      });
    case "invalid-path":
    default:
      return new SaveMediaSourceError("invalid-path", "Media path is not safe to read", {
        cause: err,
      });
  }
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/media/store.ts
Line: 228-249

Comment:
add case for `"outside-workspace"` to provide more specific error message

```suggestion
function toSaveMediaSourceError(err: SafeOpenError): SaveMediaSourceError {
  switch (err.code) {
    case "symlink":
      return new SaveMediaSourceError("invalid-path", "Media path must not be a symlink", {
        cause: err,
      });
    case "not-file":
      return new SaveMediaSourceError("not-file", "Media path is not a file", { cause: err });
    case "path-mismatch":
      return new SaveMediaSourceError("path-mismatch", "Media path changed during read", {
        cause: err,
      });
    case "too-large":
      return new SaveMediaSourceError("too-large", "Media exceeds 5MB limit", { cause: err });
    case "not-found":
      return new SaveMediaSourceError("not-found", "Media path does not exist", { cause: err });
    case "outside-workspace":
      return new SaveMediaSourceError("invalid-path", "Media path is outside workspace root", {
        cause: err,
      });
    case "invalid-path":
    default:
      return new SaveMediaSourceError("invalid-path", "Media path is not safe to read", {
        cause: err,
      });
  }
}
```

How can I resolve this? If you propose a fix, please make it concise.

@YuzuruS
Copy link
Contributor Author

YuzuruS commented Feb 28, 2026

Addressed Greptile's suggestion: added "outside-workspace" case to toSaveMediaSourceError in src/media/store.ts (da82d02). Returns "Media path is outside workspace root" instead of falling through to the generic message.

@YuzuruS
Copy link
Contributor Author

YuzuruS commented Feb 28, 2026

Hi. Thank you for maintaining openclaw. This PR is ready for review. @obviyus

YuzuruS and others added 4 commits February 28, 2026 17:59
When editing a file outside the workspace root, SafeOpenError previously
used the "invalid-path" code with the message "path escapes root". This
was indistinguishable from other invalid-path errors (hardlinks, symlinks,
non-files) and consumers often fell back to a generic "not found" message,
which was misleading.

Add a new "outside-workspace" error code with the message "file is outside
workspace root" so consumers can surface a clear, accurate error message.

- fs-safe.ts: add "outside-workspace" to SafeOpenErrorCode, use it for
  all path-escapes-root checks in openFileWithinRoot/writeFileWithinRoot
- pi-tools.read.ts: map "outside-workspace" to EACCES instead of rethrowing
- browser/paths.ts: return specific "File is outside {scopeLabel}" message
- media/server.ts: return 400 with descriptive message for outside-workspace
- fs-safe.test.ts: update traversal test expectations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address Greptile review: add explicit "outside-workspace" case to
toSaveMediaSourceError so it returns "Media path is outside workspace
root" instead of the generic "Media path is not safe to read".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@obviyus obviyus force-pushed the fix/outside-workspace-error-message branch from 8048e28 to f70ee96 Compare February 28, 2026 12:38
@obviyus obviyus merged commit f0c8603 into openclaw:main Feb 28, 2026
2 checks passed
@obviyus
Copy link
Contributor

obviyus commented Feb 28, 2026

Landed via temp rebase onto main.

  • Gate: pnpm check && pnpm build && pnpm test (check/build passed; test run had pre-existing failure in src/docker-setup.test.ts)
  • Land commit: f70ee96
  • Merge commit: f0c8603

Thanks @YuzuruS!

vincentkoc pushed a commit to Sid-Qin/openclaw that referenced this pull request Feb 28, 2026
vincentkoc pushed a commit to rylena/rylen-openclaw that referenced this pull request Feb 28, 2026
newtontech pushed a commit to newtontech/openclaw-fork that referenced this pull request Feb 28, 2026
@YuzuruS YuzuruS deleted the fix/outside-workspace-error-message branch February 28, 2026 23:08
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Mar 1, 2026
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Mar 1, 2026
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 1, 2026
ansh pushed a commit to vibecode/openclaw that referenced this pull request Mar 2, 2026
steipete pushed a commit to Sid-Qin/openclaw that referenced this pull request Mar 2, 2026
safzanpirani pushed a commit to safzanpirani/clawdbot that referenced this pull request Mar 2, 2026
steipete pushed a commit to Sid-Qin/openclaw that referenced this pull request Mar 2, 2026
robertchang-ga pushed a commit to robertchang-ga/openclaw that referenced this pull request Mar 2, 2026
robertchang-ga pushed a commit to robertchang-ga/openclaw that referenced this pull request Mar 2, 2026
execute008 pushed a commit to execute008/openclaw that referenced this pull request Mar 2, 2026
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 3, 2026
dorgonman pushed a commit to kanohorizonia/openclaw that referenced this pull request Mar 3, 2026
sachinkundu pushed a commit to sachinkundu/openclaw that referenced this pull request Mar 6, 2026
Mateljan1 pushed a commit to Mateljan1/openclaw that referenced this pull request Mar 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants