diff --git a/src/agents/provider-http-errors.test.ts b/src/agents/provider-http-errors.test.ts index 824e4c23de1..a7b17919ff1 100644 --- a/src/agents/provider-http-errors.test.ts +++ b/src/agents/provider-http-errors.test.ts @@ -136,6 +136,21 @@ describe("provider error utils", () => { ); }); + it("does not split UTF-16 surrogate pairs when truncating provider error details", async () => { + const safePrefix = "a".repeat(218); + const message = `${safePrefix}😀suffix`; + const response = new Response( + JSON.stringify({ + error: { message, code: "utf16_test" }, + }), + { status: 400 }, + ); + + await expect(assertOkOrThrowProviderError(response, "Provider API error")).rejects.toThrow( + `Provider API error (400): ${safePrefix}… [code=utf16_test]`, + ); + }); + it("keeps HTTP status metadata when error body reads fail", async () => { const response = { ok: false, diff --git a/src/agents/provider-http-errors.ts b/src/agents/provider-http-errors.ts index a052d25c5b2..3d0ba647352 100644 --- a/src/agents/provider-http-errors.ts +++ b/src/agents/provider-http-errors.ts @@ -4,6 +4,7 @@ * Transport adapters use this module to turn provider-specific response bodies, * request ids, and binary payload guardrails into stable OpenClaw error shapes. */ +import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice"; export { asFiniteNumber } from "../../packages/normalization-core/src/number-coercion.js"; import { normalizeOptionalString as trimToUndefined } from "../../packages/normalization-core/src/string-coerce.js"; import { readResponseWithLimit } from "../infra/http-body.js"; @@ -25,7 +26,7 @@ export function asObject(value: unknown): Record | undefined { /** Trims provider error details to a log- and prompt-safe preview length. */ export function truncateErrorDetail(detail: string, limit = 220): string { - return detail.length <= limit ? detail : `${detail.slice(0, limit - 1)}…`; + return detail.length <= limit ? detail : `${truncateUtf16Safe(detail, limit - 1)}…`; } /** Redacts secrets before preserving a bounded provider error body preview. */