mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-23 02:45:38 -06:00
fix(errors): drop cause text the message already states (#125687)
* fix(errors): drop cause text the message already states Cause-chain dedupe compared whole strings, so a wrapper that embeds its cause verbatim printed it twice, and an errno detail was followed by its own bare code. Skip any cause segment already contained in the accumulated message. * test(backup): stop pinning the duplicated errno suffix Both debug-view assertions required the bare code to follow the errno detail that already names it. Assert the detail itself instead, so they pin the message content rather than the redundancy. * fix(errors): narrow cause dedupe to wrapper-embedded messages Suppressing every contained segment also dropped trailing bare codes, which cron, fs, and backup tests pin deliberately as this formatter's convention. Restrict containment to cause messages so the wrapper duplication is fixed without changing the code suffix, and restore the backup assertions.
This commit is contained in:
committed by
GitHub
parent
9ad1c0c682
commit
5db8368f35
@@ -26,6 +26,27 @@ describe("formatErrorMessage", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("omits cause text the wrapper message already spells out", () => {
|
||||
// Wrappers that embed the cause verbatim printed the whole sentence twice.
|
||||
const parseFailure = new SyntaxError("JSON5: invalid character 'j' at 1:7");
|
||||
const wrapped = new Error(`Failed to parse --file as JSON5: ${parseFailure.message}`, {
|
||||
cause: parseFailure,
|
||||
});
|
||||
expect(format(wrapped)).toBe(
|
||||
"Failed to parse --file as JSON5: JSON5: invalid character 'j' at 1:7",
|
||||
);
|
||||
|
||||
// Codes keep their own segment even when the detail already names them.
|
||||
const errno = Object.assign(
|
||||
new Error("ENOENT: no such file or directory, open '/tmp/missing.json'"),
|
||||
{ code: "ENOENT" },
|
||||
);
|
||||
const notFound = new Error("--file not found: /tmp/missing.json.", { cause: errno });
|
||||
expect(format(notFound)).toBe(
|
||||
"--file not found: /tmp/missing.json. | ENOENT: no such file or directory, open '/tmp/missing.json' | ENOENT",
|
||||
);
|
||||
});
|
||||
|
||||
it("formats status/code records and structured non-Error causes", () => {
|
||||
expect(format({ status: 500, code: "EPIPE" })).toBe("status=500 code=EPIPE");
|
||||
expect(format({ status: 404 })).toBe("status=404 code=unknown");
|
||||
|
||||
@@ -87,6 +87,17 @@ export function formatErrorMessage(value: unknown, options: FormatErrorMessageOp
|
||||
formatted += ` | ${message}`;
|
||||
seenMessages.add(message);
|
||||
};
|
||||
// Wrappers routinely embed the cause verbatim ("failed to parse X: <cause.message>"),
|
||||
// which exact-match dedupe misses, so the whole sentence prints twice. Codes stay on
|
||||
// their own: a trailing bare code is this formatter's convention even when the detail
|
||||
// already names it.
|
||||
const appendCauseErrorMessage = (message: string | undefined): void => {
|
||||
if (message && formatted.includes(message)) {
|
||||
seenMessages.add(message);
|
||||
return;
|
||||
}
|
||||
appendCauseMessage(message);
|
||||
};
|
||||
if (options.includeCode) {
|
||||
const code = readProperty(value, "code");
|
||||
if (typeof code === "string" || typeof code === "number") {
|
||||
@@ -96,7 +107,7 @@ export function formatErrorMessage(value: unknown, options: FormatErrorMessageOp
|
||||
while (cause && !seen.has(cause)) {
|
||||
seen.add(cause);
|
||||
if (cause instanceof Error) {
|
||||
appendCauseMessage(cause.message);
|
||||
appendCauseErrorMessage(cause.message);
|
||||
const code = readProperty(cause, "code");
|
||||
if (typeof code === "string" || typeof code === "number") {
|
||||
appendCauseMessage(String(code));
|
||||
|
||||
Reference in New Issue
Block a user