refactor: dedupe pi compaction error formatting

This commit is contained in:
Peter Steinberger
2026-04-07 01:01:20 +01:00
parent a4253deb67
commit fef4e4621a
10 changed files with 40 additions and 42 deletions

View File

@@ -22,6 +22,7 @@ import {
resolveSessionCompactionCheckpointReason,
type CapturedCompactionCheckpointSnapshot,
} from "../../gateway/session-compaction-checkpoints.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { resolveHeartbeatSummaryForAgent } from "../../infra/heartbeat-summary.js";
import { getMachineDisplayName } from "../../infra/machine-name.js";
import { generateSecureToken } from "../../infra/secure-random.js";
@@ -1084,7 +1085,7 @@ export async function compactEmbeddedPiSessionDirect(
}
} catch (err) {
log.warn("[compaction] post-compaction truncation failed", {
errorMessage: err instanceof Error ? err.message : String(err),
errorMessage: formatErrorMessage(err),
errorStack: err instanceof Error ? err.stack : undefined,
});
}
@@ -1279,7 +1280,7 @@ export async function compactEmbeddedPiSession(
);
} catch (err) {
log.warn("before_compaction hook failed", {
errorMessage: err instanceof Error ? err.message : String(err),
errorMessage: formatErrorMessage(err),
});
}
}
@@ -1356,7 +1357,7 @@ export async function compactEmbeddedPiSession(
);
} catch (err) {
log.warn("after_compaction hook failed", {
errorMessage: err instanceof Error ? err.message : String(err),
errorMessage: formatErrorMessage(err),
});
}
}

View File

@@ -1,6 +1,7 @@
import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { OpenClawConfig } from "../../config/config.js";
import { createInternalHookEvent, triggerInternalHook } from "../../hooks/internal-hooks.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
import { getActiveMemorySearchManager } from "../../plugins/memory-runtime.js";
import { emitSessionTranscriptUpdate } from "../../sessions/transcript-events.js";
@@ -52,7 +53,7 @@ async function runPostCompactionSessionMemorySync(params: {
sessionFiles: [sessionFile],
});
} catch (err) {
log.warn(`memory sync skipped (post-compaction): ${String(err)}`);
log.warn(`memory sync skipped (post-compaction): ${formatErrorMessage(err)}`);
}
}
@@ -192,7 +193,7 @@ export async function runBeforeCompactionHooks(params: {
await triggerInternalHook(hookEvent);
} catch (err) {
log.warn("session:compact:before hook failed", {
errorMessage: err instanceof Error ? err.message : String(err),
errorMessage: formatErrorMessage(err),
errorStack: err instanceof Error ? err.stack : undefined,
});
}
@@ -213,7 +214,7 @@ export async function runBeforeCompactionHooks(params: {
);
} catch (err) {
log.warn("before_compaction hook failed", {
errorMessage: err instanceof Error ? err.message : String(err),
errorMessage: formatErrorMessage(err),
errorStack: err instanceof Error ? err.stack : undefined,
});
}
@@ -276,7 +277,7 @@ export async function runAfterCompactionHooks(params: {
await triggerInternalHook(hookEvent);
} catch (err) {
log.warn("session:compact:after hook failed", {
errorMessage: err instanceof Error ? err.message : String(err),
errorMessage: formatErrorMessage(err),
errorStack: err instanceof Error ? err.stack : undefined,
});
}
@@ -299,7 +300,7 @@ export async function runAfterCompactionHooks(params: {
);
} catch (err) {
log.warn("after_compaction hook failed", {
errorMessage: err instanceof Error ? err.message : String(err),
errorMessage: formatErrorMessage(err),
errorStack: err instanceof Error ? err.stack : undefined,
});
}

View File

@@ -21,6 +21,7 @@
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { resolveStateDir } from "../../config/paths.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { resolveProxyFetchFromEnv } from "../../infra/net/proxy-fetch.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
@@ -97,7 +98,7 @@ function writeDiskCache(map: Map<string, OpenRouterModelCapabilities>): void {
};
writeFileSync(resolveDiskCachePath(), JSON.stringify(payload), "utf-8");
} catch (err: unknown) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
log.debug(`Failed to write OpenRouter disk cache: ${message}`);
}
}
@@ -212,7 +213,7 @@ async function doFetch(): Promise<void> {
writeDiskCache(map);
log.debug(`Cached ${map.size} OpenRouter models from API`);
} catch (err: unknown) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
log.warn(`Failed to fetch OpenRouter models: ${message}`);
} finally {
clearTimeout(timeout);

View File

@@ -1,5 +1,6 @@
import type { StreamFn } from "@mariozechner/pi-agent-core";
import { createAssistantMessageEventStream, streamSimple } from "@mariozechner/pi-ai";
import { formatErrorMessage } from "../../../infra/errors.js";
import { buildStreamErrorAssistantMessage } from "../../stream-message-shared.js";
const UNHANDLED_STOP_REASON_RE = /^Unhandled stop reason:\s*(.+)$/i;
@@ -69,9 +70,7 @@ function wrapStreamHandleUnhandledStopReason(
patchUnhandledStopReasonInAssistantMessage(message);
return message;
} catch (err) {
const normalizedMessage = normalizeUnhandledStopReasonMessage(
err instanceof Error ? err.message : String(err),
);
const normalizedMessage = normalizeUnhandledStopReasonMessage(formatErrorMessage(err));
if (!normalizedMessage) {
throw err;
}
@@ -105,9 +104,7 @@ function wrapStreamHandleUnhandledStopReason(
}
return result;
} catch (err) {
const normalizedMessage = normalizeUnhandledStopReasonMessage(
err instanceof Error ? err.message : String(err),
);
const normalizedMessage = normalizeUnhandledStopReasonMessage(formatErrorMessage(err));
if (!normalizedMessage) {
throw err;
}
@@ -152,9 +149,7 @@ export function wrapStreamFnHandleSensitiveStopReason(baseFn: StreamFn): StreamF
return Promise.resolve(maybeStream).then(
(stream) => wrapStreamHandleUnhandledStopReason(model, stream),
(err) => {
const normalizedMessage = normalizeUnhandledStopReasonMessage(
err instanceof Error ? err.message : String(err),
);
const normalizedMessage = normalizeUnhandledStopReasonMessage(formatErrorMessage(err));
if (!normalizedMessage) {
throw err;
}
@@ -164,9 +159,7 @@ export function wrapStreamFnHandleSensitiveStopReason(baseFn: StreamFn): StreamF
}
return wrapStreamHandleUnhandledStopReason(model, maybeStream);
} catch (err) {
const normalizedMessage = normalizeUnhandledStopReasonMessage(
err instanceof Error ? err.message : String(err),
);
const normalizedMessage = normalizeUnhandledStopReasonMessage(formatErrorMessage(err));
if (!normalizedMessage) {
throw err;
}

View File

@@ -1,5 +1,6 @@
import path from "node:path";
import type { ImageContent } from "@mariozechner/pi-ai";
import { formatErrorMessage } from "../../../infra/errors.js";
import { assertNoWindowsNetworkPath, safeFileURLToPath } from "../../../infra/local-file-access.js";
import type { PromptImageOrderEntry } from "../../../media/prompt-image-order.js";
import { resolveMediaBufferPath, getMediaDir } from "../../../media/store.js";
@@ -379,7 +380,7 @@ export async function loadImageFromRef(
return { type: "image", data, mimeType };
} catch (err) {
log.debug(
`Native image: failed to load media-uri ${ref.resolved}: ${err instanceof Error ? err.message : String(err)}`,
`Native image: failed to load media-uri ${ref.resolved}: ${formatErrorMessage(err)}`,
);
return null;
}
@@ -402,7 +403,7 @@ export async function loadImageFromRef(
targetPath = resolved.resolved;
} catch (err) {
log.debug(
`Native image: sandbox validation failed for ${ref.resolved}: ${err instanceof Error ? err.message : String(err)}`,
`Native image: sandbox validation failed for ${ref.resolved}: ${formatErrorMessage(err)}`,
);
return null;
}
@@ -440,9 +441,7 @@ export async function loadImageFromRef(
return { type: "image", data, mimeType };
} catch (err) {
// Log the actual error for debugging (size limits, network failures, etc.)
log.debug(
`Native image: failed to load ${ref.resolved}: ${err instanceof Error ? err.message : String(err)}`,
);
log.debug(`Native image: failed to load ${ref.resolved}: ${formatErrorMessage(err)}`);
return null;
}
}

View File

@@ -6,6 +6,7 @@ import {
isHeartbeatOkResponse,
isHeartbeatUserMessage,
} from "../../auto-reply/heartbeat-filter.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { log } from "./logger.js";
/**
@@ -47,7 +48,7 @@ export async function truncateSessionAfterCompaction(params: {
try {
sm = SessionManager.open(sessionFile);
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
const reason = formatErrorMessage(err);
log.warn(`[session-truncation] Failed to open session file: ${reason}`);
return { truncated: false, entriesRemoved: 0, reason };
}
@@ -205,7 +206,7 @@ export async function truncateSessionAfterCompaction(params: {
await fs.copyFile(sessionFile, params.archivePath);
log.info(`[session-truncation] Archived pre-truncation file to ${params.archivePath}`);
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
const reason = formatErrorMessage(err);
log.warn(`[session-truncation] Failed to archive: ${reason}`);
}
}
@@ -225,7 +226,7 @@ export async function truncateSessionAfterCompaction(params: {
} catch {
// Ignore cleanup errors
}
const reason = err instanceof Error ? err.message : String(err);
const reason = formatErrorMessage(err);
log.warn(`[session-truncation] Failed to write truncated file: ${reason}`);
return { truncated: false, entriesRemoved: 0, reason };
}

View File

@@ -1,5 +1,6 @@
import type { AgentMessage, StreamFn } from "@mariozechner/pi-agent-core";
import { createAssistantMessageEventStream } from "@mariozechner/pi-ai";
import { formatErrorMessage } from "../../infra/errors.js";
import { log } from "./logger.js";
type AssistantContentBlock = Extract<AgentMessage, { role: "assistant" }>["content"][number];
@@ -218,7 +219,7 @@ function shouldRecoverAnthropicThinkingError(
error: unknown,
sessionMeta: RecoverySessionMeta,
): boolean {
const message = error instanceof Error ? error.message : String(error);
const message = formatErrorMessage(error);
if (!THINKING_BLOCK_ERROR_PATTERN.test(message)) {
return false;
}

View File

@@ -1,6 +1,7 @@
import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { TextContent } from "@mariozechner/pi-ai";
import { SessionManager } from "@mariozechner/pi-coding-agent";
import { formatErrorMessage } from "../../infra/errors.js";
import { emitSessionTranscriptUpdate } from "../../sessions/transcript-events.js";
import { acquireSessionWriteLock } from "../session-write-lock.js";
import { log } from "./logger.js";
@@ -583,7 +584,7 @@ export function truncateOversizedToolResultsInSessionManager(params: {
try {
return truncateOversizedToolResultsInExistingSessionManager(params);
} catch (err) {
const errMsg = err instanceof Error ? err.message : String(err);
const errMsg = formatErrorMessage(err);
log.warn(`[tool-result-truncation] Failed to truncate: ${errMsg}`);
return { truncated: false, truncatedCount: 0, reason: errMsg };
}
@@ -609,7 +610,7 @@ export async function truncateOversizedToolResultsInSession(params: {
sessionKey: params.sessionKey,
});
} catch (err) {
const errMsg = err instanceof Error ? err.message : String(err);
const errMsg = formatErrorMessage(err);
log.warn(`[tool-result-truncation] Failed to truncate: ${errMsg}`);
return { truncated: false, truncatedCount: 0, reason: errMsg };
} finally {

View File

@@ -5,6 +5,7 @@ import type {
TranscriptRewriteRequest,
TranscriptRewriteResult,
} from "../../context-engine/types.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { emitSessionTranscriptUpdate } from "../../sessions/transcript-events.js";
import { getRawSessionAppendMessage } from "../session-tool-result-guard.js";
import { acquireSessionWriteLock } from "../session-write-lock.js";
@@ -218,7 +219,7 @@ export async function rewriteTranscriptEntriesInSessionFile(params: {
}
return result;
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
const reason = formatErrorMessage(err);
log.warn(`[transcript-rewrite] failed: ${reason}`);
return {
changed: false,

View File

@@ -4,6 +4,7 @@ import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { ExtensionAPI, ExtensionContext, FileOperations } from "@mariozechner/pi-coding-agent";
import { extractSections } from "../../auto-reply/reply/post-compaction-context.js";
import { openBoundaryFile } from "../../infra/boundary-file-read.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import {
hasMeaningfulConversationContent,
@@ -639,7 +640,7 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
}
requestAuth = await modelRegistry.getApiKeyAndHeaders(model);
} catch (err) {
const error = err instanceof Error ? err.message : String(err);
const error = formatErrorMessage(err);
log.warn(
`Compaction safeguard: request credentials unavailable; cancelling compaction. ${error}`,
);
@@ -745,9 +746,9 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
});
} catch (droppedError) {
log.warn(
`Compaction safeguard: failed to summarize dropped messages, continuing without: ${
droppedError instanceof Error ? droppedError.message : String(droppedError)
}`,
`Compaction safeguard: failed to summarize dropped messages, continuing without: ${formatErrorMessage(
droppedError,
)}`,
);
}
}
@@ -848,9 +849,7 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
if (lastSuccessfulSummary && attempt > 0) {
log.warn(
`Compaction safeguard: quality retry failed on attempt ${attempt + 1}; ` +
`keeping last successful summary: ${
attemptError instanceof Error ? attemptError.message : String(attemptError)
}`,
`keeping last successful summary: ${formatErrorMessage(attemptError)}`,
);
summary = lastSuccessfulSummary;
break;
@@ -927,7 +926,7 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
},
};
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
const message = formatErrorMessage(error);
log.warn(
`Compaction summarization failed; cancelling compaction to preserve history: ${message}`,
);