mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:30:43 +00:00
fix: handle ENOSPC file watcher errors gracefully (#73357)
Merged via squash.
Prepared head SHA: ce2dd6ed3e
Co-authored-by: solodmd <51304754+solodmd@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
This commit is contained in:
@@ -446,6 +446,12 @@ export abstract class MemoryManagerSyncOps {
|
||||
this.watcher.on("change", markDirty);
|
||||
this.watcher.on("unlink", markDirty);
|
||||
this.watcher.on("unlinkDir", markDirty);
|
||||
this.watcher.on("error", (err) => {
|
||||
// File watcher errors (e.g., ENOSPC) should not crash the gateway.
|
||||
// Log the error and continue - memory search still works without auto-sync.
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
log.warn(`memory watcher error: ${message}`);
|
||||
});
|
||||
}
|
||||
|
||||
protected ensureSessionListener() {
|
||||
|
||||
@@ -9,9 +9,9 @@ import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vites
|
||||
|
||||
type WatchIgnoredFn = (watchPath: string, stats?: { isDirectory?: () => boolean }) => boolean;
|
||||
|
||||
const { createdWatchers, watchMock } = vi.hoisted(() => {
|
||||
type WatchEvent = "add" | "change" | "unlink" | "unlinkDir";
|
||||
type WatchCallback = () => void;
|
||||
const { createdWatchers, memoryLoggerWarn, watchMock } = vi.hoisted(() => {
|
||||
type WatchEvent = "add" | "change" | "unlink" | "unlinkDir" | "error";
|
||||
type WatchCallback = (value?: unknown) => void;
|
||||
function createMockWatcher() {
|
||||
const handlers = new Map<WatchEvent, WatchCallback[]>();
|
||||
const watcher = {
|
||||
@@ -20,9 +20,9 @@ const { createdWatchers, watchMock } = vi.hoisted(() => {
|
||||
return watcher;
|
||||
}),
|
||||
close: vi.fn(async () => undefined),
|
||||
emit: (event: WatchEvent) => {
|
||||
emit: (event: WatchEvent, value?: unknown) => {
|
||||
for (const callback of handlers.get(event) ?? []) {
|
||||
callback();
|
||||
callback(value);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -31,6 +31,7 @@ const { createdWatchers, watchMock } = vi.hoisted(() => {
|
||||
const watchers: Array<ReturnType<typeof createMockWatcher>> = [];
|
||||
const result = {
|
||||
createdWatchers: watchers,
|
||||
memoryLoggerWarn: vi.fn(),
|
||||
watchMock: vi.fn(() => {
|
||||
const watcher = createMockWatcher();
|
||||
watchers.push(watcher);
|
||||
@@ -42,6 +43,18 @@ const { createdWatchers, watchMock } = vi.hoisted(() => {
|
||||
return result;
|
||||
});
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/memory-core-host-engine-foundation", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("openclaw/plugin-sdk/memory-core-host-engine-foundation")>();
|
||||
return {
|
||||
...actual,
|
||||
createSubsystemLogger: (subsystem: string) => ({
|
||||
...actual.createSubsystemLogger(subsystem),
|
||||
warn: memoryLoggerWarn,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./sqlite-vec.js", () => ({
|
||||
loadSqliteVecExtension: async () => ({ ok: false, error: "sqlite-vec disabled in tests" }),
|
||||
}));
|
||||
@@ -246,4 +259,16 @@ describe("memory watcher config", () => {
|
||||
expect(syncSpy).toHaveBeenCalledWith({ reason: "watch" });
|
||||
},
|
||||
);
|
||||
|
||||
it("attaches a logging non-throwing watcher error listener", async () => {
|
||||
await setupWatcherWorkspace({ name: "notes.md", contents: "hello" });
|
||||
const cfg = createWatcherConfig();
|
||||
|
||||
await expectWatcherManager(cfg);
|
||||
|
||||
const watcher = createdWatchers[0];
|
||||
expect(watcher?.on).toHaveBeenCalledWith("error", expect.any(Function));
|
||||
expect(() => watcher?.emit("error", new Error("watcher error: ENOSPC"))).not.toThrow();
|
||||
expect(memoryLoggerWarn).toHaveBeenCalledWith("memory watcher error: watcher error: ENOSPC");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user