Skip to content

Fix Windows false-positive path-mismatch in safe local file read#25700

Closed
alexmao1024 wants to merge 1 commit intoopenclaw:mainfrom
alexmao1024:fix/windows-fs-safe-dev-check
Closed

Fix Windows false-positive path-mismatch in safe local file read#25700
alexmao1024 wants to merge 1 commit intoopenclaw:mainfrom
alexmao1024:fix/windows-fs-safe-dev-check

Conversation

@alexmao1024
Copy link
Copy Markdown

@alexmao1024 alexmao1024 commented Feb 24, 2026

Summary

  • Avoid false-positive path-mismatch on Windows caused by dev mismatch between handle.stat() and fs.lstat() for the same file.
  • Keep existing security checks on non-Windows platforms.

Root cause

On Windows, FileHandle.stat() can report a different dev value than fs.lstat()/fs.stat() for the same file. The current check treats this as a path change and throws, which blocks local media reads (e.g., inbound images).

Fix

Gate dev comparison by platform:

  • if process.platform === "win32", only compare ino.
  • otherwise, keep dev + ino check.

Testing

  • Reproduced failure on Windows 11 + Node 22.14.0: inbound image read fails with Local media path is not safe to read / path changed during read.
  • After this change, inbound images load successfully and vision works as expected.

Related

Issue #25699

Greptile Summary

Fixed Windows false-positive path-mismatch errors by skipping dev comparison on Windows platform. On Windows, FileHandle.stat() and fs.lstat() can report different dev values for the same file, causing legitimate file reads to fail. The fix follows the same pattern used in safe-open-sync.ts (commit 04bcabc) which addressed an identical Windows dev mismatch issue.

Confidence Score: 4/5

  • Safe to merge with minor observation about approach consistency
  • The fix correctly addresses the Windows-specific dev mismatch issue and maintains security by continuing to check ino on all platforms. The implementation follows an established pattern from safe-open-sync.ts. However, there's a slight inconsistency in approach: this PR uses && process.platform !== "win32" while safe-open-sync.ts uses || (process.platform === "win32" && (left.dev === 0 || right.dev === 0)), though both are functionally correct for the Windows case.
  • No files require special attention

Last reviewed commit: f9fdf0c

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

Copy link
Copy Markdown
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.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +65 to +66
const devMismatch = stat.dev !== lstat.dev && process.platform !== "win32";
if (stat.ino !== lstat.ino || devMismatch) {
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.

This approach (skipping dev check entirely on Windows) differs from safe-open-sync.ts:24 which only ignores dev mismatch when either value is 0. Both work for the Windows false-positive issue, but the safe-open-sync.ts approach is more precise since it only relaxes the check when the mismatch is actually happening. Consider aligning with that pattern for consistency:

Suggested change
const devMismatch = stat.dev !== lstat.dev && process.platform !== "win32";
if (stat.ino !== lstat.ino || devMismatch) {
const devMismatch = stat.dev !== lstat.dev && !(process.platform === "win32" && (stat.dev === 0 || lstat.dev === 0));
if (stat.ino !== lstat.ino || devMismatch) {

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/fs-safe.ts
Line: 65-66

Comment:
This approach (skipping `dev` check entirely on Windows) differs from `safe-open-sync.ts:24` which only ignores `dev` mismatch when either value is `0`. Both work for the Windows false-positive issue, but the `safe-open-sync.ts` approach is more precise since it only relaxes the check when the mismatch is actually happening. Consider aligning with that pattern for consistency:

```suggestion
    const devMismatch = stat.dev !== lstat.dev && !(process.platform === "win32" && (stat.dev === 0 || lstat.dev === 0));
    if (stat.ino !== lstat.ino || devMismatch) {
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

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

@steipete
Copy link
Copy Markdown
Contributor

Superseded by #25917, now merged to main (merge commit 943b8f171a78fda44ba065dcec29da27b8c072c0).

What landed from this line of work:

  • shared file-identity helper used by both async and sync safe-open paths
  • Windows dev=0 fallback parity (instead of async/sync drift)
  • strict dev checking preserved when both sides are non-zero
  • focused regression tests (src/infra/file-identity.test.ts) to lock behavior

Thank you for surfacing and narrowing the root cause quickly; this directly informed the final landed fix.

@steipete steipete closed this Feb 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants