fix: avoid crash on memory embeddings errors (#1004)

This commit is contained in:
Peter Steinberger
2026-01-17 09:45:45 +00:00
parent a6deb0d9d5
commit 1a4313c2aa
6 changed files with 122 additions and 26 deletions

View File

@@ -0,0 +1,62 @@
import { describe, expect, it, vi } from "vitest";
vi.mock("../../memory/index.js", () => {
return {
getMemorySearchManager: async () => {
return {
manager: {
search: async () => {
throw new Error("openai embeddings failed: 429 insufficient_quota");
},
readFile: async () => {
throw new Error("path required");
},
status: () => ({
files: 0,
chunks: 0,
dirty: true,
workspaceDir: "/tmp",
dbPath: "/tmp/index.sqlite",
provider: "openai",
model: "text-embedding-3-small",
requestedProvider: "openai",
}),
},
};
},
};
});
import { createMemoryGetTool, createMemorySearchTool } from "./memory-tool.js";
describe("memory tools", () => {
it("does not throw when memory_search fails (e.g. embeddings 429)", async () => {
const cfg = { agents: { list: [{ id: "main", default: true }] } };
const tool = createMemorySearchTool({ config: cfg });
expect(tool).not.toBeNull();
if (!tool) throw new Error("tool missing");
const result = await tool.execute("call_1", { query: "hello" });
expect(result.details).toEqual({
results: [],
disabled: true,
error: "openai embeddings failed: 429 insufficient_quota",
});
});
it("does not throw when memory_get fails", async () => {
const cfg = { agents: { list: [{ id: "main", default: true }] } };
const tool = createMemoryGetTool({ config: cfg });
expect(tool).not.toBeNull();
if (!tool) throw new Error("tool missing");
const result = await tool.execute("call_2", { path: "memory/NOPE.md" });
expect(result.details).toEqual({
path: "memory/NOPE.md",
text: "",
disabled: true,
error: "path required",
});
});
});

View File

@@ -47,18 +47,23 @@ export function createMemorySearchTool(options: {
if (!manager) {
return jsonResult({ results: [], disabled: true, error });
}
const results = await manager.search(query, {
maxResults,
minScore,
sessionKey: options.agentSessionKey,
});
const status = manager.status();
return jsonResult({
results,
provider: status.provider,
model: status.model,
fallback: status.fallback,
});
try {
const results = await manager.search(query, {
maxResults,
minScore,
sessionKey: options.agentSessionKey,
});
const status = manager.status();
return jsonResult({
results,
provider: status.provider,
model: status.model,
fallback: status.fallback,
});
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
return jsonResult({ results: [], disabled: true, error: message });
}
},
};
}
@@ -91,12 +96,17 @@ export function createMemoryGetTool(options: {
if (!manager) {
return jsonResult({ path: relPath, text: "", disabled: true, error });
}
const result = await manager.readFile({
relPath,
from: from ?? undefined,
lines: lines ?? undefined,
});
return jsonResult(result);
try {
const result = await manager.readFile({
relPath,
from: from ?? undefined,
lines: lines ?? undefined,
});
return jsonResult(result);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
return jsonResult({ path: relPath, text: "", disabled: true, error: message });
}
},
};
}

View File

@@ -75,8 +75,14 @@ export function registerMemoryCli(program: Command) {
defaultRuntime.log(error ?? "Memory search disabled.");
return;
}
await manager.sync({ reason: "cli", force: opts.force });
defaultRuntime.log("Memory index updated.");
try {
await manager.sync({ reason: "cli", force: opts.force });
defaultRuntime.log("Memory index updated.");
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
defaultRuntime.error(`Memory index failed: ${message}`);
process.exitCode = 1;
}
});
memory
@@ -105,10 +111,18 @@ export function registerMemoryCli(program: Command) {
defaultRuntime.log(error ?? "Memory search disabled.");
return;
}
const results = await manager.search(query, {
maxResults: opts.maxResults,
minScore: opts.minScore,
});
let results: Awaited<ReturnType<typeof manager.search>>;
try {
results = await manager.search(query, {
maxResults: opts.maxResults,
minScore: opts.minScore,
});
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
defaultRuntime.error(`Memory search failed: ${message}`);
process.exitCode = 1;
return;
}
if (opts.json) {
defaultRuntime.log(JSON.stringify({ results }, null, 2));
return;

View File

@@ -178,4 +178,10 @@ async function main() {
}
}
void main();
void main().catch((err) => {
console.error(
"[clawdbot] Gateway daemon failed:",
err instanceof Error ? (err.stack ?? err.message) : err,
);
process.exit(1);
});

View File

@@ -70,4 +70,7 @@ async function main() {
await program.parseAsync(process.argv);
}
void main();
void main().catch((err) => {
console.error("[clawdbot] Relay failed:", err instanceof Error ? (err.stack ?? err.message) : err);
process.exit(1);
});