refactor: dedupe memory-core error formatting

This commit is contained in:
Peter Steinberger
2026-04-07 00:43:14 +01:00
parent ab6aa28049
commit 3417dbabf4
10 changed files with 40 additions and 29 deletions

View File

@@ -1,5 +1,6 @@
import fs from "node:fs/promises";
import path from "node:path";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
// ── Types ──────────────────────────────────────────────────────────────
@@ -286,7 +287,7 @@ export async function generateAndAppendDreamNarrative(params: {
} catch (err) {
// Narrative generation is best-effort — never fail the parent phase.
params.logger.warn(
`memory-core: narrative generation failed for ${params.data.phase} phase: ${err instanceof Error ? err.message : String(err)}`,
`memory-core: narrative generation failed for ${params.data.phase} phase: ${formatErrorMessage(err)}`,
);
} finally {
// Clean up the transient session.

View File

@@ -1,4 +1,5 @@
import fs from "node:fs/promises";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import {
enforceEmbeddingMaxInputTokens,
hasNonTextEmbeddingParts,
@@ -414,7 +415,7 @@ export abstract class MemoryManagerEmbeddingOps extends MemoryManagerSyncOps {
try {
return await params.run();
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
if (this.isBatchTimeoutError(message)) {
log.warn(`memory embeddings: ${params.provider} batch timed out; retrying once`);
try {
@@ -444,7 +445,7 @@ export abstract class MemoryManagerEmbeddingOps extends MemoryManagerSyncOps {
await this.resetBatchFailureCount();
return result;
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
const attempts = (err as { batchAttempts?: number }).batchAttempts ?? 1;
const forceDisable = /asyncBatchEmbedContent not available/i.test(message);
const failure = await this.recordBatchFailure({
@@ -636,7 +637,7 @@ export abstract class MemoryManagerEmbeddingOps extends MemoryManagerSyncOps {
? await this.embedChunksWithBatch(chunks, entry, options.source)
: await this.embedChunksInBatches(chunks);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
if (
"kind" in entry &&
entry.kind === "multimodal" &&

View File

@@ -1,3 +1,5 @@
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
type MemoryEmbeddingTextPart = {
type: "text";
text: string;
@@ -111,7 +113,7 @@ export async function runMemoryEmbeddingRetryLoop<T>(params: {
try {
return await params.run();
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
if (!params.isRetryable(message) || attempt >= params.maxAttempts) {
throw err;
}

View File

@@ -1,4 +1,5 @@
import type { DatabaseSync } from "node:sqlite";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import {
createSubsystemLogger,
type OpenClawConfig,
@@ -49,7 +50,7 @@ export function isMemoryReadonlyDbError(err: unknown): boolean {
messages.add(normalized);
};
pushValue(err instanceof Error ? err.message : String(err));
pushValue(formatErrorMessage(err));
if (err && typeof err === "object") {
const record = err as Record<string, unknown>;
pushValue(record.message);

View File

@@ -4,6 +4,7 @@ import fs from "node:fs/promises";
import path from "node:path";
import type { DatabaseSync } from "node:sqlite";
import chokidar, { FSWatcher } from "chokidar";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import {
buildCaseInsensitiveExtensionGlob,
classifyMemoryMultimodalPath,
@@ -184,7 +185,7 @@ export abstract class MemoryManagerSyncOps {
try {
ready = (await this.vectorReady) || false;
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
this.vector.available = false;
this.vector.loadError = message;
this.vectorReady = null;
@@ -217,7 +218,7 @@ export abstract class MemoryManagerSyncOps {
this.vector.available = true;
return true;
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
this.vector.available = false;
this.vector.loadError = message;
log.warn(`sqlite-vec unavailable: ${message}`);
@@ -245,7 +246,7 @@ export abstract class MemoryManagerSyncOps {
try {
this.db.exec(`DROP TABLE IF EXISTS ${VECTOR_TABLE}`);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
log.debug(`Failed to drop ${VECTOR_TABLE}: ${message}`);
}
}
@@ -1002,7 +1003,7 @@ export abstract class MemoryManagerSyncOps {
this.sessionsDirty = false;
}
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
const reason = formatErrorMessage(err);
const activated =
this.shouldFallbackOnError(reason) && (await this.activateFallbackProvider(reason));
if (activated) {

View File

@@ -1,3 +1,4 @@
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import type { MemorySyncProgressUpdate } from "openclaw/plugin-sdk/memory-core-host-engine-storage";
type TargetedSyncProgress = {
@@ -67,7 +68,7 @@ export async function runMemoryTargetedSessionSync(params: {
}),
};
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
const reason = formatErrorMessage(err);
const activated =
params.shouldFallbackOnError(reason) && (await params.activateFallbackProvider(reason));
if (!activated) {

View File

@@ -1,5 +1,6 @@
import type { DatabaseSync } from "node:sqlite";
import { type FSWatcher } from "chokidar";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import {
createSubsystemLogger,
resolveAgentDir,
@@ -776,7 +777,7 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem
await this.embedBatchWithRetry(["ping"]);
return { ok: true };
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
return { ok: false, error: message };
}
}

View File

@@ -4,6 +4,7 @@ import os from "node:os";
import path from "node:path";
import readline from "node:readline";
import chokidar, { type FSWatcher } from "chokidar";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import { withFileLock } from "openclaw/plugin-sdk/file-lock";
import {
createSubsystemLogger,
@@ -431,7 +432,7 @@ export class QmdMemoryManager implements MemorySearchManager {
try {
await this.removeCollection(collection.name);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
if (!this.isCollectionMissingError(message)) {
log.warn(`qmd collection remove failed for ${collection.name}: ${message}`);
}
@@ -445,7 +446,7 @@ export class QmdMemoryManager implements MemorySearchManager {
pattern: collection.pattern,
});
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
if (this.isCollectionAlreadyExistsError(message)) {
const rebound = await this.tryRebindConflictingCollection({
collection,
@@ -579,7 +580,7 @@ export class QmdMemoryManager implements MemorySearchManager {
await this.removeCollection(legacyName);
existing.delete(legacyName);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
if (!this.isCollectionMissingError(message)) {
log.warn(`qmd collection remove failed for ${legacyName}: ${message}`);
}
@@ -637,7 +638,7 @@ export class QmdMemoryManager implements MemorySearchManager {
}
private isMissingCollectionSearchError(err: unknown): boolean {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
return this.isCollectionMissingError(message) && message.toLowerCase().includes("collection");
}
@@ -794,7 +795,7 @@ export class QmdMemoryManager implements MemorySearchManager {
}
private shouldRepairNullByteCollectionError(err: unknown): boolean {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
const lower = message.toLowerCase();
return (
(lower.includes("enotdir") ||
@@ -806,7 +807,7 @@ export class QmdMemoryManager implements MemorySearchManager {
}
private shouldRepairDuplicateDocumentConstraint(err: unknown): boolean {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
const lower = message.toLowerCase();
return (
lower.includes("unique constraint failed") &&
@@ -1155,7 +1156,7 @@ export class QmdMemoryManager implements MemorySearchManager {
: "QMD index has 0 vectors; semantic search is unavailable until embeddings finish";
return this.vectorAvailable;
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
this.vectorAvailable = false;
this.vectorStatusDetail = `QMD status probe failed: ${message}`;
return false;
@@ -1360,7 +1361,7 @@ export class QmdMemoryManager implements MemorySearchManager {
if (this.isSqliteBusyError(err)) {
return true;
}
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
const normalized = message.toLowerCase();
return normalized.includes("timed out");
}
@@ -1614,7 +1615,7 @@ export class QmdMemoryManager implements MemorySearchManager {
}
private isQueryToolNotFoundError(err: unknown): boolean {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
const detail = message.match(/ failed \(code \d+\): ([\s\S]*)$/)?.[1];
if (!detail) {
return false;
@@ -2518,13 +2519,13 @@ export class QmdMemoryManager implements MemorySearchManager {
}
private isSqliteBusyError(err: unknown): boolean {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
const normalized = message.toLowerCase();
return normalized.includes("sqlite_busy") || normalized.includes("database is locked");
}
private isUnsupportedQmdOptionError(err: unknown): boolean {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
const normalized = message.toLowerCase();
return (
normalized.includes("unknown flag") ||
@@ -2536,7 +2537,7 @@ export class QmdMemoryManager implements MemorySearchManager {
}
private createQmdBusyError(err: unknown): Error {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
return new Error(`qmd index busy while reading results: ${message}`);
}

View File

@@ -1,3 +1,4 @@
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import {
createSubsystemLogger,
resolveAgentWorkspaceDir,
@@ -104,7 +105,7 @@ export async function getMemorySearchManager(params: {
return { manager: wrapper };
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
log.warn(`qmd memory unavailable; falling back to builtin: ${message}`);
}
}
@@ -115,7 +116,7 @@ export async function getMemorySearchManager(params: {
const manager = await MemoryIndexManager.get(params);
return { manager };
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
return { manager: null, error: message };
}
}
@@ -197,7 +198,7 @@ class FallbackMemoryManager implements MemorySearchManager {
return await this.deps.primary.search(query, opts);
} catch (err) {
this.primaryFailed = true;
this.lastError = err instanceof Error ? err.message : String(err);
this.lastError = formatErrorMessage(err);
log.warn(`qmd memory failed; switching to builtin index: ${this.lastError}`);
await this.deps.primary.close?.().catch(() => {});
// Evict the failed wrapper so the next request can retry QMD with a fresh manager.
@@ -302,7 +303,7 @@ class FallbackMemoryManager implements MemorySearchManager {
return null;
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
log.warn(`memory fallback unavailable: ${message}`);
return null;
}

View File

@@ -1,3 +1,4 @@
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import {
jsonResult,
readNumberParam,
@@ -239,7 +240,7 @@ export function createMemorySearchTool(options: {
mode: searchMode,
});
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
return jsonResult(buildMemorySearchUnavailableResult(message));
}
},