Skip to content

fix(ui): i18n locale before render + fix: tool_use mismatch suggest /new (#46366, #46365)#46373

Open
xuwei-xy wants to merge 1 commit intoopenclaw:mainfrom
xuwei-xy:batch5-pr46366-pr46365-v2
Open

fix(ui): i18n locale before render + fix: tool_use mismatch suggest /new (#46366, #46365)#46373
xuwei-xy wants to merge 1 commit intoopenclaw:mainfrom
xuwei-xy:batch5-pr46366-pr46365-v2

Conversation

@xuwei-xy
Copy link
Copy Markdown

@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui agents Agent runtime and tooling size: S labels Mar 14, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 14, 2026

Greptile Summary

This PR bundles two bug fixes: (1) it detects tool_use/tool_result history-corruption errors from the Anthropic API and surfaces a friendly recovery message directing users to /new, and (2) it ensures the i18n locale is fully loaded before the first render of the Lit application component.

Key changes:

  • errors.ts: Adds isToolUseResultMismatchError / TOOL_USE_RESULT_MISMATCH_RE and wires it into both formatAssistantErrorText and sanitizeUserFacingText (behind the existing errorContext guard).
  • translate.ts: Makes loadLocale async and exposes i18n.ready: Promise<void> so callers can await initial locale resolution.
  • app.ts: Introduces a localeReady state flag; the component renders nothing until both i18n.ready and any explicit setLocale call from app settings have settled, preventing a flash of untranslated UI.
  • Tests are added or updated for all three areas.

Issues found:

  • The first alternative of TOOL_USE_RESULT_MISMATCH_RE is just tool_use_id, which matches any string containing that substring. Unrelated validation errors (e.g. "Invalid format for tool_use_id", "tool_use_id is required") will incorrectly display "Conversation history corruption detected" — misleading advice that may prompt users to discard their session unnecessarily.
  • In app.ts, i18n.ready (reads from localStorage/navigator) and settingsLocale (reads from app settings) are run concurrently via Promise.all. If the two locale sources disagree, the non-deterministic resolution order of setLocale means the final rendered locale could come from either source.

Confidence Score: 3/5

  • Mostly safe to merge; two non-critical issues should be addressed before landing.
  • The i18n fix is clean and the test improvement is strictly better. The tool-mismatch fix solves a real user-facing problem. However, the overly broad tool_use_id regex alternative can cause unrelated validation errors to display an incorrect "history corruption" message, and the concurrent Promise.all locale-loading pattern introduces a low-probability but silent race condition when localStorage and app settings disagree on the active locale.
  • src/agents/pi-embedded-helpers/errors.ts (regex breadth) and ui/src/ui/app.ts (concurrent locale loading).
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-helpers/errors.ts
Line: 838-839

Comment:
**Overly broad `tool_use_id` alternative in regex**

The first branch of the regex — `tool_use_id` — matches any string that merely contains the substring `"tool_use_id"`, with no further constraints. This means error messages completely unrelated to a history-corruption mismatch (e.g. `"Invalid format for tool_use_id"`, `"tool_use_id is required"`, or `"Unsupported tool_use_id type"`) will all return `"Conversation history corruption detected. Please use /new to start a fresh session."`, which is incorrect advice and will confuse users.

The other two alternatives (`tool_result.*corresponding.*tool_use` and `unexpected.*tool.*block`) are adequately specific. The first alternative should be tightened to require additional context, for example:

```suggestion
const TOOL_USE_RESULT_MISMATCH_RE =
  /tool_result.*tool_use_id.*not found|tool_result.*corresponding.*tool_use|unexpected.*tool.*block/i;
```

This matches the actual Anthropic API error strings exercised by the tests (`"tool_result block with tool_use_id … not found in the immediately preceding assistant message"`) while avoiding false positives on generic validation errors that happen to reference the field name.

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

---

This is a comment left during a code review.
Path: ui/src/ui/app.ts
Line: 119-133

Comment:
**Potential race condition between `i18n.ready` and `settingsLocale`**

Both `i18n.ready` (which loads the locale persisted in `localStorage` / derived from `navigator.language`) and `settingsLocale` (which loads the locale from app `settings`) are kicked off concurrently via `Promise.all`. Because `setLocale` is not re-entrant, whichever resolves *last* wins and sets `this.locale`.

If the two sources disagree — e.g. `localStorage` says `"zh-CN"` and `settings.locale` says `"fr"` — the final rendered locale will be whichever translation finishes loading later, which is non-deterministic. App settings should be the authoritative source, so `settingsLocale` ought to be applied *after* `i18n.ready` resolves to avoid being overwritten:

```
i18n.ready
  .then(() => isSupportedLocale(this.settings.locale) ? i18n.setLocale(this.settings.locale) : Promise.resolve())
  .then(() => { this.localeReady = true; }, () => { this.localeReady = true; });
```

This is a low-probability edge case (requires the two stores to diverge), but it would be an invisible, intermittent bug.

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

Last reviewed commit: 55fa530

Comment on lines +838 to +839
const TOOL_USE_RESULT_MISMATCH_RE =
/tool_use_id|tool_result.*corresponding.*tool_use|unexpected.*tool.*block/i;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overly broad tool_use_id alternative in regex

The first branch of the regex — tool_use_id — matches any string that merely contains the substring "tool_use_id", with no further constraints. This means error messages completely unrelated to a history-corruption mismatch (e.g. "Invalid format for tool_use_id", "tool_use_id is required", or "Unsupported tool_use_id type") will all return "Conversation history corruption detected. Please use /new to start a fresh session.", which is incorrect advice and will confuse users.

The other two alternatives (tool_result.*corresponding.*tool_use and unexpected.*tool.*block) are adequately specific. The first alternative should be tightened to require additional context, for example:

Suggested change
const TOOL_USE_RESULT_MISMATCH_RE =
/tool_use_id|tool_result.*corresponding.*tool_use|unexpected.*tool.*block/i;
const TOOL_USE_RESULT_MISMATCH_RE =
/tool_result.*tool_use_id.*not found|tool_result.*corresponding.*tool_use|unexpected.*tool.*block/i;

This matches the actual Anthropic API error strings exercised by the tests ("tool_result block with tool_use_id … not found in the immediately preceding assistant message") while avoiding false positives on generic validation errors that happen to reference the field name.

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

Comment:
**Overly broad `tool_use_id` alternative in regex**

The first branch of the regex — `tool_use_id` — matches any string that merely contains the substring `"tool_use_id"`, with no further constraints. This means error messages completely unrelated to a history-corruption mismatch (e.g. `"Invalid format for tool_use_id"`, `"tool_use_id is required"`, or `"Unsupported tool_use_id type"`) will all return `"Conversation history corruption detected. Please use /new to start a fresh session."`, which is incorrect advice and will confuse users.

The other two alternatives (`tool_result.*corresponding.*tool_use` and `unexpected.*tool.*block`) are adequately specific. The first alternative should be tightened to require additional context, for example:

```suggestion
const TOOL_USE_RESULT_MISMATCH_RE =
  /tool_result.*tool_use_id.*not found|tool_result.*corresponding.*tool_use|unexpected.*tool.*block/i;
```

This matches the actual Anthropic API error strings exercised by the tests (`"tool_result block with tool_use_id … not found in the immediately preceding assistant message"`) while avoiding false positives on generic validation errors that happen to reference the field name.

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

Comment thread ui/src/ui/app.ts
Comment on lines 119 to 133
constructor() {
super();
if (isSupportedLocale(this.settings.locale)) {
void i18n.setLocale(this.settings.locale);
}
const settingsLocale = isSupportedLocale(this.settings.locale)
? i18n.setLocale(this.settings.locale)
: Promise.resolve();
Promise.all([i18n.ready, settingsLocale]).then(
() => {
this.localeReady = true;
},
() => {
// Locale load failed; render with the default locale.
this.localeReady = true;
},
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Potential race condition between i18n.ready and settingsLocale

Both i18n.ready (which loads the locale persisted in localStorage / derived from navigator.language) and settingsLocale (which loads the locale from app settings) are kicked off concurrently via Promise.all. Because setLocale is not re-entrant, whichever resolves last wins and sets this.locale.

If the two sources disagree — e.g. localStorage says "zh-CN" and settings.locale says "fr" — the final rendered locale will be whichever translation finishes loading later, which is non-deterministic. App settings should be the authoritative source, so settingsLocale ought to be applied after i18n.ready resolves to avoid being overwritten:

i18n.ready
  .then(() => isSupportedLocale(this.settings.locale) ? i18n.setLocale(this.settings.locale) : Promise.resolve())
  .then(() => { this.localeReady = true; }, () => { this.localeReady = true; });

This is a low-probability edge case (requires the two stores to diverge), but it would be an invisible, intermittent bug.

Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/ui/app.ts
Line: 119-133

Comment:
**Potential race condition between `i18n.ready` and `settingsLocale`**

Both `i18n.ready` (which loads the locale persisted in `localStorage` / derived from `navigator.language`) and `settingsLocale` (which loads the locale from app `settings`) are kicked off concurrently via `Promise.all`. Because `setLocale` is not re-entrant, whichever resolves *last* wins and sets `this.locale`.

If the two sources disagree — e.g. `localStorage` says `"zh-CN"` and `settings.locale` says `"fr"` — the final rendered locale will be whichever translation finishes loading later, which is non-deterministic. App settings should be the authoritative source, so `settingsLocale` ought to be applied *after* `i18n.ready` resolves to avoid being overwritten:

```
i18n.ready
  .then(() => isSupportedLocale(this.settings.locale) ? i18n.setLocale(this.settings.locale) : Promise.resolve())
  .then(() => { this.localeReady = true; }, () => { this.localeReady = true; });
```

This is a low-probability edge case (requires the two stores to diverge), but it would be an invisible, intermittent bug.

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

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 28, 2026

Codex review: needs changes before merge.

Summary
The PR adds awaitable Control UI i18n startup readiness, delays initial render until locale loading completes, and rewrites tool_use/tool_result mismatch errors to suggest /new, with focused tests.

Reproducibility: yes. Source inspection on current main shows both original gaps remain, and the PR-introduced defects are visible in the patch: a standalone tool_use_id regex branch and concurrent locale-loading promises.

Next step before merge
The remaining blockers are concrete file-local fixes plus a rebase or replacement branch, so a repair lane can safely attempt them without a product decision.

Security
Cleared: The diff only changes UI locale startup, agent error formatting/helper exports, and tests, with no dependency, CI, secret, permission, package, or code execution surface changes.

Review findings

  • [P2] Tighten the tool_use mismatch classifier — src/agents/pi-embedded-helpers/errors.ts:839
  • [P2] Apply settings locale after startup readiness — ui/src/ui/app.ts:119-133
  • [P3] Add the required changelog entry — CHANGELOG.md:1
Review details

Best possible solution:

Land a rebased focused update that serializes locale precedence, narrows mismatch detection to real tool_result/tool_use pairing failures, updates the current sanitizer/error-helper modules, adds false-positive coverage, and includes the required changelog entry.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection on current main shows both original gaps remain, and the PR-introduced defects are visible in the patch: a standalone tool_use_id regex branch and concurrent locale-loading promises.

Is this the best way to solve the issue?

No. The direction is appropriate, but this implementation should not merge until settings locale is applied after startup readiness, mismatch detection requires tool_result/tool_use pairing context, and the patch is rebased onto the current sanitizer split.

Full review comments:

  • [P2] Tighten the tool_use mismatch classifier — src/agents/pi-embedded-helpers/errors.ts:839
    The standalone tool_use_id alternative matches unrelated provider or schema errors that merely mention that field, so users can be told their session is corrupted and to run /new when the actual error is different. Require tool_result/tool_use pairing context such as missing, not found, or corresponding tool_use before rewriting the message.
    Confidence: 0.88
  • [P2] Apply settings locale after startup readiness — ui/src/ui/app.ts:119-133
    The patch starts initial locale loading and the settings locale load concurrently. If browser storage or navigator and app settings disagree, the slower promise wins and can render the app in the wrong language; wait for i18n.ready, then apply settings.locale before setting localeReady.
    Confidence: 0.84
  • [P3] Add the required changelog entry — CHANGELOG.md:1
    This PR changes user-facing Control UI startup behavior and user-facing agent error recovery copy, so repo policy requires a CHANGELOG.md fix entry before merge. The entry can avoid guessed or bot-only attribution if the final credited human author is unclear.
    Confidence: 0.8

Overall correctness: patch is incorrect
Overall confidence: 0.88

Acceptance criteria:

  • pnpm test src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts ui/src/i18n/test/translate.test.ts
  • pnpm exec oxfmt --check --threads=1 src/agents/pi-embedded-helpers.ts src/agents/pi-embedded-helpers/errors.ts src/agents/pi-embedded-helpers/sanitize-user-facing-text.ts src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts ui/src/i18n/lib/translate.ts ui/src/i18n/test/translate.test.ts ui/src/ui/app.ts CHANGELOG.md
  • pnpm check:changed in Testbox before handoff

What I checked:

  • PR state: GitHub API reports this PR is open, unmerged, non-draft, head 55fa5300fdc92e8035200fadfb44f35a3cc6655e, and currently mergeable: false, so it is not obsolete merged work and needs rebase/repair before merge. (55fa5300fdc9)
  • Broad regex in PR diff: The PR adds TOOL_USE_RESULT_MISMATCH_RE = /tool_use_id|.../, so unrelated provider/schema messages that merely mention tool_use_id would be rewritten as conversation-history corruption. (src/agents/pi-embedded-helpers/errors.ts:839, 55fa5300fdc9)
  • Concurrent locale sources in PR diff: The PR starts i18n.ready and settingsLocale together in Promise.all, so if browser/persisted locale and app settings disagree, the slower setLocale call can determine the final rendered language. (ui/src/ui/app.ts:119, 55fa5300fdc9)
  • Current main lacks awaitable i18n startup: I18nManager still calls this.loadLocale() from the constructor and loadLocale() fire-and-forgets void this.setLocale(initialLocale), leaving no ready promise for first-render gating. (ui/src/i18n/lib/translate.ts:21, 9c37cfcbdbf7)
  • Current main renders before locale readiness: OpenClawApp applies settings.locale asynchronously in the constructor and render() immediately delegates to renderApp, so first render is not gated on locale loading. (ui/src/ui/app.ts:148, 9c37cfcbdbf7)
  • Current main lacks focused tool_result/tool_use recovery: REPLAY_INVALID_RE handles previous response ids, missing tool input, role ordering, and connection mismatch, but not tool_result/tool_use_id pairing failures; current formatting falls back to schema or raw request rejection handling instead of the /new recovery hint. (src/agents/pi-embedded-helpers/errors.ts:333, 9c37cfcbdbf7)

Likely related people:

  • chilu18: Commit 053b0df... is the recent feature-history point for loading a saved Control UI locale on startup, which is the behavior this PR refines by making startup readiness awaitable. (role: introduced current locale startup behavior; confidence: high; commits: 053b0df7d431; files: ui/src/i18n/lib/translate.ts)
  • BunsDev: Recent commits by this author changed ui/src/ui/app.ts around Control UI session and chat behavior, making them a useful routing candidate for the app-constructor/render side of the locale fix. (role: recent Control UI maintainer; confidence: medium; commits: 05c9492bff0f, 37aebf612b83, 5fce2f6b0fb1; files: ui/src/ui/app.ts)
  • vincentkoc: This author recently extracted the user-facing text sanitizer and also touched ui/src/ui/app.ts, both adjacent to the current rebase and review findings. (role: recent sanitizer and UI maintainer; confidence: high; commits: 35664d5447a4, d69d610bf004; files: src/agents/pi-embedded-helpers/sanitize-user-facing-text.ts, ui/src/ui/app.ts)
  • steipete: Recent history shows maintainer work on i18n tests, WebUI app behavior, agent failure observability, and sanitizer behavior near both halves of this PR. (role: recent maintainer across both affected surfaces; confidence: high; commits: 0218045818ec, 68359cacbf58, 0e586bb48a31; files: ui/src/i18n/lib/translate.ts, ui/src/ui/app.ts, src/agents/pi-embedded-helpers/errors.ts)
  • dallylee: Commit bd7418d... added a related replay-invalid classifier for connection mismatch errors in the same error-formatting area. (role: adjacent replay-error classifier contributor; confidence: medium; commits: bd7418d4e91f; files: src/agents/pi-embedded-helpers/errors.ts)

Remaining risk / open question:

  • The source branch is currently unmergeable and stale relative to current main's sanitizer split, so a repair likely needs a rebase or replacement PR.
  • No executable tests were run because this review was constrained to read-only inspection.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 9c37cfcbdbf7.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling app: web-ui App: web-ui size: S

Projects

None yet

1 participant