fix(ui): i18n locale before render + fix: tool_use mismatch suggest /new (#46366, #46365)#46373
fix(ui): i18n locale before render + fix: tool_use mismatch suggest /new (#46366, #46365)#46373xuwei-xy wants to merge 1 commit intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR bundles two bug fixes: (1) it detects Key changes:
Issues found:
Confidence Score: 3/5
Prompt To Fix All With AIThis 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 |
| const TOOL_USE_RESULT_MISMATCH_RE = | ||
| /tool_use_id|tool_result.*corresponding.*tool_use|unexpected.*tool.*block/i; |
There was a problem hiding this 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:
| 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.| 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; | ||
| }, | ||
| ); | ||
| } |
There was a problem hiding this 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.
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.|
Codex review: needs changes before merge. Summary Reproducibility: yes. Source inspection on current main shows both original gaps remain, and the PR-introduced defects are visible in the patch: a standalone Next step before merge Security Review findings
Review detailsBest 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 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:
Overall correctness: patch is incorrect Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 9c37cfcbdbf7. |
Combines fixes from upstream PRs:
Merged from openclaw/openclaw PRs.