Skip to content

fix(errors): prevent billing false positive in sanitizeUserFacingText#13467

Open
lailoo wants to merge 3 commits intoopenclaw:mainfrom
lailoo:fix/billing-false-positive-13434
Open

fix(errors): prevent billing false positive in sanitizeUserFacingText#13467
lailoo wants to merge 3 commits intoopenclaw:mainfrom
lailoo:fix/billing-false-positive-13434

Conversation

@lailoo
Copy link
Contributor

@lailoo lailoo commented Feb 10, 2026

Summary

Fixes #13434

Problem

sanitizeUserFacingText() unconditionally applies isBillingErrorMessage() to all user-facing text. The isBillingErrorMessage function uses a broad heuristic that matches any text containing both "billing" and one of "payment", "upgrade", "credits", or "plan". This causes assistant-generated content discussing billing/payment topics (e.g., gym membership billing details) to be replaced with the generic billing error warning.

Fix

Add a shouldRewriteBillingText() guard function (matching the existing shouldRewriteContextOverflowText() pattern) that distinguishes real billing errors from assistant prose:

  1. Precise billing patterns (402, insufficient credits, credit balance, payment required, plans & billing) are rewritten unconditionally — these are unambiguous error strings.
  2. Broad heuristic matches (billing + payment/upgrade/credits/plan) are only rewritten when the text looks like a raw error message (API payload, HTTP error, error prefix, or single-sentence without markdown/paragraphs).

Reproduction & Verification

Unit-level (direct function call):

Before fix (main branch) — Bug reproduced:

--- Assistant content (should NOT be rewritten) ---
  "**Billing:** Processed through ABC Financial Services..."  ❌ FALSE POSITIVE
  "The gym membership billing cycle runs monthly..."           ❌ FALSE POSITIVE
  "Here is a summary of the billing and payment options..."    ❌ FALSE POSITIVE

After fix — All verified:

--- Assistant content (should NOT be rewritten) ---
  ✅ PASS (all assistant content samples preserved)

--- Real billing errors (SHOULD be rewritten) ---
  ✅ PASS: "insufficient credits"
  ✅ PASS: "billing: please upgrade your plan"
  ✅ PASS: "Your credit balance is too low"

Integration-level (real gateway reply pipeline):

Added normalizeReplyPayload integration tests in src/auto-reply/reply/normalize-reply.test.ts that exercise the full reply normalization pipeline (normalizeReplyPayloadsanitizeUserFacingText):

Before fix (main branch) — Bug reproduced through real pipeline:

normalizeReplyPayload({ text: "**Billing:** ... payments ..." })
  → text: "⚠️ API provider returned a billing error..."  ❌ FALSE POSITIVE

After fix — Pipeline preserves assistant content:

normalizeReplyPayload({ text: "**Billing:** ... payments ..." })
  → text: "**Billing:** Processed through ABC Financial Services..."  ✅ PRESERVED

normalizeReplyPayload({ text: "insufficient credits" })
  → text: "⚠️ API provider returned a billing error..."  ✅ REWRITTEN

Effect on User Experience

Before fix:
Sub-agent researches gym membership details → output contains "Billing: ... payments" → parent receives "⚠️ API provider returned a billing error" instead of actual findings.

After fix:
Assistant content discussing billing/payment topics is delivered as-is. Real billing errors (402, insufficient credits, etc.) are still correctly caught and rewritten.

Testing

  • ✅ 14 unit tests pass (12 existing + 2 new regression tests in sanitizeuserfacingtext.test.ts)
  • ✅ 2 new integration tests pass (normalizeReplyPayload pipeline in normalize-reply.test.ts)
  • isBillingErrorMessage() unchanged — error classification for failover/logging still works

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 file reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 169 to 172
const hasMultipleSentences = /[.!?]\s+[A-Z]/.test(raw);
const hasMarkdown = /[*_#[\]|]/.test(raw);
const hasParagraphs = raw.includes("\n\n");
return !hasMultipleSentences && !hasMarkdown && !hasParagraphs;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lowercase sentence false positive
hasMultipleSentences uses /[.!?]\s+[A-Z]/ (errors.ts:169), so multi-sentence prose where the next sentence starts lowercase (e.g., "... . payment is ...") is treated as single-sentence and will still be rewritten if it matches the broad billing heuristic. This undermines the goal of not rewriting assistant prose; consider using a sentence boundary check that doesn’t rely on capitalization (or a different prose-vs-error heuristic).

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-helpers/errors.ts
Line: 169:172

Comment:
**Lowercase sentence false positive**
`hasMultipleSentences` uses `/[.!?]\s+[A-Z]/` (errors.ts:169), so multi-sentence prose where the next sentence starts lowercase (e.g., "... . payment is ...") is treated as single-sentence and will still be rewritten if it matches the broad billing heuristic. This undermines the goal of not rewriting assistant prose; consider using a sentence boundary check that doesn’t rely on capitalization (or a different prose-vs-error heuristic).

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 168 to 172
// Single-sentence short texts without markdown are likely error messages.
const hasMultipleSentences = /[.!?]\s+[A-Z]/.test(raw);
const hasMarkdown = /[*_#[\]|]/.test(raw);
const hasParagraphs = raw.includes("\n\n");
return !hasMultipleSentences && !hasMarkdown && !hasParagraphs;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One-sentence prose still rewritten
The fallback return !hasMultipleSentences && !hasMarkdown && !hasParagraphs; (errors.ts:168-172) will rewrite any single-sentence assistant content that happens to contain billing plus payment/upgrade/credits/plan (the broad heuristic). If the intent is to only rewrite raw error strings on the broad path, this condition is too permissive; a one-sentence paragraph of prose will be replaced by BILLING_ERROR_USER_MESSAGE.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-helpers/errors.ts
Line: 168:172

Comment:
**One-sentence prose still rewritten**
The fallback `return !hasMultipleSentences && !hasMarkdown && !hasParagraphs;` (errors.ts:168-172) will rewrite any single-sentence assistant content that happens to contain `billing` plus `payment/upgrade/credits/plan` (the broad heuristic). If the intent is to only rewrite raw error strings on the broad path, this condition is too permissive; a one-sentence paragraph of prose will be replaced by `BILLING_ERROR_USER_MESSAGE`.

How can I resolve this? If you propose a fix, please make it concise.

@lailoo lailoo force-pushed the fix/billing-false-positive-13434 branch from 6c5aa15 to 3b6fe1f Compare February 11, 2026 02:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: S trusted-contributor Contributor with 4+ merged PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positive: Sub-agent output about billing/payments incorrectly flagged as API error

1 participant

Comments