fix: narrow file watcher transient errors

This commit is contained in:
Altay
2026-05-02 15:58:07 +03:00
parent 31b135a5ad
commit b6c8058d46
3 changed files with 27 additions and 10 deletions

View File

@@ -113,7 +113,7 @@ Docs: https://docs.openclaw.ai
- Codex harness: forward OpenClaw workspace bootstrap files such as `SOUL.md` through native Codex config instructions while leaving `AGENTS.md` to Codex project-doc discovery. Fixes #76273. Thanks @zknicker.
- Parallels/Windows update smoke: escape the stale post-swap import regex in the generated PowerShell script so expected `ERR_MODULE_NOT_FOUND` update handoffs continue to post-update health checks. (#75315)
- Slack: allow draft preview streaming in top-level DMs when `replyToMode` is `off` while keeping Slack native streaming and assistant thread status gated on reply threads. Fixes #56480. (#56544) Thanks @HangGlidersRule.
- Control UI/chat: remove the delete-confirm popover outside-click listener on every dismiss path, so Cancel, Delete, outside clicks, and same-button toggles no longer leave stale document listeners behind. Refs #75590 and #69982. Thanks @Ricardo-M-L.
- Memory-core: treat exhausted file watcher limits as non-fatal for builtin memory auto-sync while preserving fatal handling for unrelated disk-full errors. (#73357) Thanks @solodmd.
## 2026.5.2

View File

@@ -289,6 +289,13 @@ describe("isTransientFileWatchError", () => {
expect(isTransientFileWatchError(error)).toBe(false);
});
it("returns false for message-only disk full without watch indicator", () => {
expect(isTransientFileWatchError(new Error("write failed: no space left on device"))).toBe(
false,
);
expect(isTransientFileWatchError(new Error("ENOSPC: no space left on device"))).toBe(false);
});
it("returns true for 'no space left on device' message with watcher context", () => {
const error = new Error("file watcher: no space left on device");
expect(isTransientFileWatchError(error)).toBe(true);
@@ -386,4 +393,13 @@ describe("isTransientUnhandledRejectionError", () => {
});
expect(isTransientUnhandledRejectionError(error)).toBe(false);
});
it("returns false for code-less disk full messages without watch indicator", () => {
expect(
isTransientUnhandledRejectionError(new Error("write failed: no space left on device")),
).toBe(false);
expect(isTransientUnhandledRejectionError(new Error("ENOSPC: no space left on device"))).toBe(
false,
);
});
});

View File

@@ -365,6 +365,13 @@ export function isTransientFileWatchError(err: unknown): boolean {
return false;
}
const hasFileWatchSignal = (message: string) =>
message.includes("inotify") ||
message.includes("watcher") ||
message.includes("file watcher") ||
message.includes("watch limit") ||
message.includes("max watches");
for (const candidate of collectNestedUnhandledErrorCandidates(err)) {
// Skip non-object candidates early
if (!candidate || typeof candidate !== "object") {
@@ -379,13 +386,7 @@ export function isTransientFileWatchError(err: unknown): boolean {
// ENOSPC requires both the code AND a watch/inotify message indicator
// to avoid misclassifying general disk-full errors as transient watcher errors.
if (code === "ENOSPC") {
if (
message.includes("inotify") ||
message.includes("watcher") ||
message.includes("file watcher") ||
message.includes("watch limit") ||
message.includes("max watches")
) {
if (hasFileWatchSignal(message)) {
return true;
}
// ENOSPC without watch indicator is not classified here
@@ -397,8 +398,8 @@ export function isTransientFileWatchError(err: unknown): boolean {
continue;
}
if (
message.includes("no space left on device") ||
message.includes("enosp") ||
((message.includes("no space left on device") || message.includes("enosp")) &&
hasFileWatchSignal(message)) ||
message.includes("inotify watches") ||
message.includes("file watcher") ||
message.includes("watcher error")