fix(microsoft-foundry): keep Azure CLI error truncation UTF-16 safe (#102604)

* fix(microsoft-foundry): keep Azure CLI error truncation UTF-16 safe

Replace `.slice(0, 300)` with `truncateUtf16Safe(normalized, 300)` in
summarizeAzErrorMessage to prevent surrogate pair corruption.

Ref. lsr911 pattern — mechanical substitution, no behavior change.

* test(microsoft-foundry): cover UTF-16-safe CLI errors

* test(microsoft-foundry): assert complete CLI error

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
huangjianxiong 2026-07-09 18:49:35 +08:00 committed by GitHub
parent 0a8677ec99
commit c335ecdb73
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 18 additions and 1 deletions

View file

@ -4,6 +4,7 @@ import {
normalizeOptionalString,
normalizeStringifiedOptionalString,
} from "openclaw/plugin-sdk/string-coerce-runtime";
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
import type { AzAccessToken, AzAccount } from "./shared.js";
import { COGNITIVE_SERVICES_RESOURCE } from "./shared.js";
@ -34,7 +35,7 @@ function summarizeAzErrorMessage(raw: string): string {
if (/aadsts\d+/i.test(normalized)) {
return "Azure login failed for the selected tenant. Re-run `az login --use-device-code` and confirm the tenant is correct.";
}
return normalized.slice(0, 300);
return truncateUtf16Safe(normalized, 300);
}
function buildAzCommandError(error: Error, stderr: string, stdout: string): Error {

View file

@ -1830,6 +1830,22 @@ describe("microsoft-foundry plugin", () => {
await expect(getAccessTokenResultAsync()).rejects.toThrow("Azure CLI is not logged in");
});
it("keeps bounded Azure CLI error details UTF-16 safe", async () => {
const prefix = "x".repeat(299);
execFileMock.mockImplementationOnce(
(
_file: unknown,
_args: unknown,
_options: unknown,
callback: (error: Error | null, stdout: string, stderr: string) => void,
) => callback(new Error("az failed"), "", `${prefix}😀tail`),
);
await expect(getAccessTokenResultAsync()).rejects.toMatchObject({
message: `az failed: ${prefix}`,
});
});
it("deletes legacy provider-level secret refs", () => {
const secretRef = {
source: "env" as const,