fix(errors): hide internal agent metadata

This commit is contained in:
t8y2 committed 2026-08-03 13:56:18 +08:00
1 parent fa17c01c3b
commit 16920188ee
5 files changed
+65 -15

No files matched your search

+34 -1
View File
@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
import { describe, expect, it as test } from "vitest";
import { createI18n } from "vue-i18n";
import { translateBackendError, type BackendErrorTranslate } from "@/i18n/backend-errors";
import { BackendErrorException, normalizeBackendError } from "@/lib/backend/errorUtils";
import { BackendErrorException, formatError, normalizeBackendError, sanitizeBackendErrorMessage } from "@/lib/backend/errorUtils";
import en from "@/i18n/locales/en";
import es from "@/i18n/locales/es";
import it from "@/i18n/locales/it";
@@ -171,6 +171,23 @@ describe("backend error translation", () => {
expect(translateBackendError(t, "some driver specific failure")).toBe("some driver specific failure");
});
test("hides internal Agent error data from user-facing messages", () => {
const t = translatorFor("zh-CN");
const message = 'Agent RPC error (-1): driver: bad connection\nDBX_AGENT_ERROR_DATA:{"category":null,"retryable":null,"sessionDisposition":null,"stage":null,"operationOutcome":null,"agentSessionId":"e1a4d0a2907947b8adf31abb10c4dff9"}';
const expected = "Agent RPC error (-1): driver: bad connection";
expect(sanitizeBackendErrorMessage(message)).toBe(expected);
expect(formatError(new Error(message))).toBe(expected);
expect(new BackendErrorException(message).message).toBe(expected);
expect(translateBackendError(t, message)).toBe(expected);
});
test("preserves marker-like database messages without valid internal data", () => {
const message = "database returned\nDBX_AGENT_ERROR_DATA:not-json";
expect(sanitizeBackendErrorMessage(message)).toBe(message);
});
test("normalizes Error and structural message objects before translation", () => {
const t = translatorFor("zh-CN");
const message = "file does not exist: /tmp/missing.sqlite";
@@ -213,6 +230,22 @@ describe("backend error translation", () => {
expect(translateBackendError(t, error)).toBe(`${t(error.messageKey)}\n\n${error.detail}`);
});
test("hides internal Agent error data from structured error details", () => {
const t = translatorFor("zh-CN");
const detail = 'driver: bad connection\nDBX_AGENT_ERROR_DATA:{"category":null,"agentSessionId":"session-1"}';
const error = {
version: 1,
code: "DBX-JDBC-9001",
messageKey: "backendErrors.jdbc.legacyFailure",
messageParams: {},
source: "jdbcAgentLegacy",
operationOutcome: "unknown",
detail,
} as const;
expect(translateBackendError(t, error)).toBe(`${t(error.messageKey)}\n\ndriver: bad connection`);
});
test.each([
["array params", { messageParams: ["execute"] }],
["nested params", { messageParams: { stage: { name: "execute" } } }],
+5 -5
View File
@@ -1,4 +1,4 @@
import { normalizeBackendError, type BackendError } from "@/lib/backend/errorUtils";
import { normalizeBackendError, sanitizeBackendErrorMessage, type BackendError } from "@/lib/backend/errorUtils";
/**
* Minimal shape of a translate function, satisfied by both `useI18n().t` inside
@@ -115,15 +115,15 @@ const paramNames: Record<string, string | string[]> = {
};
function backendErrorMessage(error: unknown): string {
if (typeof error === "string") return error;
if (error && typeof error === "object" && "message" in error && typeof error.message === "string") return error.message;
return String(error);
if (typeof error === "string") return sanitizeBackendErrorMessage(error);
if (error && typeof error === "object" && "message" in error && typeof error.message === "string") return sanitizeBackendErrorMessage(error.message);
return sanitizeBackendErrorMessage(String(error));
}
function translateStructuredBackendError(t: BackendErrorTranslate, error: BackendError): string {
const translated = t(error.messageKey, error.messageParams);
const summary = translated !== error.messageKey ? translated : t("backendErrors.unknown");
const detail = error.detail?.trim();
const detail = error.detail ? sanitizeBackendErrorMessage(error.detail).trim() : undefined;
return detail && detail !== summary ? `${summary}\n\n${detail}` : summary;
}