From 5e423b596cdfb91f05a22869e7e9c08ffc5c407d Mon Sep 17 00:00:00 2001 From: niceysam Date: Sun, 22 Feb 2026 03:17:39 +0900 Subject: [PATCH] fix: remove false-positive billing error rewrite on normal assistant text (openclaw#17834) thanks @niceysam Verified: - pnpm install --frozen-lockfile - pnpm build - pnpm check - pnpm test:macmini Co-authored-by: niceysam <256747835+niceysam@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> --- CHANGELOG.md | 1 + ...helpers.sanitizeuserfacingtext.e2e.test.ts | 9 +++++++-- src/agents/pi-embedded-helpers/errors.ts | 19 ------------------- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a8b32a31..c34c1e87f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ Docs: https://docs.openclaw.ai - Security/Exec: block `sort --compress-program` in `tools.exec.safeBins` policy so allowlist-mode safe-bin checks cannot be used to bypass approval and spawn external programs. Thanks @tdjackey for reporting. - Doctor/State integrity: only require/create the OAuth credentials directory when WhatsApp or pairing-backed channels are configured, and downgrade fresh-install missing-dir noise to an informational warning. +- Agents/Sanitization: stop rewriting billing-shaped assistant text outside explicit error context so normal replies about billing/credits/payment are preserved across messaging channels. (#17834, fixes #11359) - Security/Agents: cap embedded Pi runner outer retry loop with a higher profile-aware dynamic limit (32-160 attempts) and return an explicit `retry_limit` error payload when retries never converge, preventing unbounded internal retry cycles (`GHSA-76m6-pj3w-v7mf`). - Telegram: detect duplicate bot-token ownership across Telegram accounts at startup/status time, mark secondary accounts as not configured with an explicit fix message, and block duplicate account startup before polling to avoid endless `getUpdates` conflict loops. - Agents/Tool images: include source filenames in `agents/tool-images` resize logs so compression events can be traced back to specific files. diff --git a/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.e2e.test.ts b/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.e2e.test.ts index ee24dac09..8c0af5cc2 100644 --- a/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.e2e.test.ts +++ b/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.e2e.test.ts @@ -72,9 +72,14 @@ describe("sanitizeUserFacingText", () => { expect(sanitizeUserFacingText(text)).toBe(text); }); - it("rewrites billing error-shaped text", () => { + it("does not rewrite billing error-shaped text without errorContext", () => { const text = "billing: please upgrade your plan"; - expect(sanitizeUserFacingText(text)).toContain("billing error"); + expect(sanitizeUserFacingText(text)).toBe(text); + }); + + it("rewrites billing error-shaped text with errorContext", () => { + const text = "billing: please upgrade your plan"; + expect(sanitizeUserFacingText(text, { errorContext: true })).toContain("billing error"); }); it("sanitizes raw API error payloads", () => { diff --git a/src/agents/pi-embedded-helpers/errors.ts b/src/agents/pi-embedded-helpers/errors.ts index b24cec955..8b6e93421 100644 --- a/src/agents/pi-embedded-helpers/errors.ts +++ b/src/agents/pi-embedded-helpers/errors.ts @@ -244,18 +244,6 @@ function shouldRewriteContextOverflowText(raw: string): boolean { ); } -function shouldRewriteBillingText(raw: string): boolean { - if (!isBillingErrorMessage(raw)) { - return false; - } - return ( - isRawApiErrorPayload(raw) || - isLikelyHttpErrorText(raw) || - ERROR_PREFIX_RE.test(raw) || - BILLING_ERROR_HEAD_RE.test(raw) - ); -} - type ErrorPayload = Record; function isErrorPayloadObject(payload: unknown): payload is ErrorPayload { @@ -552,13 +540,6 @@ export function sanitizeUserFacingText(text: string, opts?: { errorContext?: boo } } - // Preserve legacy behavior for explicit billing-head text outside known - // error contexts (e.g., "billing: please upgrade your plan"), while - // keeping conversational billing mentions untouched. - if (shouldRewriteBillingText(trimmed)) { - return BILLING_ERROR_USER_MESSAGE; - } - // Strip leading blank lines (including whitespace-only lines) without clobbering indentation on // the first content line (e.g. markdown/code blocks). const withoutLeadingEmptyLines = stripped.replace(/^(?:[ \t]*\r?\n)+/, "");