fix(smart-mind-map): prevent OWUI v0.10+ frontend crash on action early-return#102
Conversation
…ly-return (issue #101) On OpenWebUI v0.10+, clicking the Smart Mind Map action crashed the frontend with `Cannot read properties of undefined (reading 'content')` at Chat.svelte (chatActionHandler). Root cause: the action's three early-return paths (no messages / no extractable content / text too short) appended a *new* message dict without an `id` field to body["messages"]. The OWUI v0.10+ frontend iterates the returned messages and does `history.messages[message.id].content` for each entry; when `message.id` is undefined, the lookup returns undefined and accessing `.content` throws. Fix: update the *existing* last message's content (it already carries a valid `id`) instead of appending a new id-less message. The frontend preserves the original content as `originalContent` before applying the update, so nothing is lost. The error is still surfaced via the notification emitter. - Add `_set_last_message_content` helper with safe fallbacks - Rewire all three early-return paths to use it - Add regression tests verifying every returned message carries an `id` - Bump version to 1.0.2
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
✅ Plugin Version Check / 插件版本检查版本更新检测通过!PR 包含版本变化和更新说明。 Version check passed! PR contains version changes and update description. 版本变化 / Version ChangesPlugin Updates
This comment was generated automatically. / 此评论由自动生成。 |
There was a problem hiding this comment.
Code Review
This pull request addresses a frontend crash (issue #101) on OpenWebUI v0.10+ by updating the last message's content in-place instead of appending a new id-less message, and adds a regression test suite. The feedback recommends making the message update helper more robust by ensuring an id field is always present (with a fallback), updating the regression tests to verify this fallback, and syncing the version bump to 1.0.2 across all required documentation files per the repository style guide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| last = messages[-1] | ||
| if not isinstance(last, dict): | ||
| messages[-1] = {"role": "assistant"} | ||
| messages[-1]["content"] = content |
There was a problem hiding this comment.
If last is not a dictionary, it is replaced with {"role": "assistant"}. However, this new dictionary does not contain an id field. Since the core issue (#101) is that the frontend crashes when a message lacks an id field, returning a message without an id will still trigger the same crash.
Furthermore, if last is a dictionary but somehow lacks an id field, it will also cause a crash.
To make this robust and defensive, we should ensure that the updated message always has an id field, falling back to body.get("id") (which represents the current assistant message ID) if it is missing.
| last = messages[-1] | |
| if not isinstance(last, dict): | |
| messages[-1] = {"role": "assistant"} | |
| messages[-1]["content"] = content | |
| last = messages[-1] | |
| if not isinstance(last, dict): | |
| last = {"role": "assistant"} | |
| messages[-1] = last | |
| if "id" not in last and isinstance(body, dict) and "id" in body: | |
| last["id"] = body["id"] | |
| last["content"] = content |
| author_url: https://github.com/Fu-Jie/openwebui-extensions | ||
| funding_url: https://github.com/open-webui | ||
| version: 1.0.1 | ||
| version: 1.0.2 |
There was a problem hiding this comment.
According to the repository style guide's Documentation Sync rule, when changing a plugin and bumping its version, you must update the version and release notes in all associated documentation files.
Currently, smart_mind_map.py has been bumped to 1.0.2, but README.md and README_CN.md still reference 1.0.1. Please ensure the following files are updated to maintain consistency:
plugins/actions/smart-mind-map/README.mdplugins/actions/smart-mind-map/README_CN.mddocs/plugins/actions/smart-mind-map.mddocs/plugins/actions/smart-mind-map.zh.mddocs/plugins/actions/index.mddocs/plugins/actions/index.zh.md
References
- Must update all documentation files (README, README_CN, docs, index) with the new version when changing a plugin. (link)
| def test_handles_non_dict_last_message(self): | ||
| action = _make_action() | ||
| body = {"messages": ["not-a-dict"]} | ||
| action._set_last_message_content(body, "recovered") | ||
| assert body["messages"][-1]["content"] == "recovered" |
There was a problem hiding this comment.
To align with the defensive fallback id logic added to _set_last_message_content, we should update this test to verify that the fallback id is correctly assigned when the last message is not a dictionary.
| def test_handles_non_dict_last_message(self): | |
| action = _make_action() | |
| body = {"messages": ["not-a-dict"]} | |
| action._set_last_message_content(body, "recovered") | |
| assert body["messages"][-1]["content"] == "recovered" | |
| def test_handles_non_dict_last_message(self): | |
| action = _make_action() | |
| body = {"id": "fallback-id", "messages": ["not-a-dict"]} | |
| action._set_last_message_content(body, "recovered") | |
| assert body["messages"][-1]["content"] == "recovered" | |
| assert body["messages"][-1]["id"] == "fallback-id" |
…I v0.10+ (issue #101) OWUI v0.10+ stores assistant replies in a structured `output` field and leaves the flat `content` empty. The frontend-crash fix in v1.0.2 stopped the `Cannot read properties of undefined (reading 'content')` error, but the plugin still hit the "No content found to export!" early-return path because `_extract_text_content` saw an empty `content` — so the action wasn't actually usable on v0.10+. This is the second layer of issue #101, mirroring the approach in PR #103 (rbb-dev's fix for export_to_excel / export_to_docx): - New `_extract_text_from_output` rebuilds assistant text from the structured `output` using OWUI's own `convert_output_to_messages` (reasoning excluded), so we follow upstream schema changes instead of hand-rolling a parser. - New `_recover_message_content` resolves text in three steps: 1. existing flat `content` when non-empty (pre-v0.10, or OWUI backfilled) 2. inline `msg.output` when present on the payload 3. DB lookup via `ChatMessages.get_message_by_id(chat_id-message_id)` for when the payload doesn't carry `output` at all Recovered text is backfilled onto `msg["content"]` so downstream code reads it uniformly. - Guarded imports: on older OWUI versions without `ChatMessages` / `convert_output_to_messages`, the recovery silently no-ops and the plugin falls back to the original `content`-only path — no breakage. - Action's aggregation loop now calls `_recover_message_content` instead of `_extract_text_content`. - Bump version 1.0.2 → 1.0.3. Tests: 22 cases (was 11). New `TestRecoverContentFromOutput` (8 unit tests) and `TestActionUsesOutputRecovery` (3 end-to-end tests) cover inline output, DB lookup, missing record, reasoning exclusion, empty fallback, and the id-invariant under the full action flow. Verified the new tests fail on v1.0.2 code (8 failures, methods absent) and pass on v1.0.3.
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
…w-version users - Add `required_open_webui_version: 0.10.2` metadata (aligns with PR #103's approach for export_to_excel/docx). OWUI enforces this at install time. - Add 4 backward-compat regression tests (TestPreV010BackwardCompat) that simulate pre-v0.10 environment (ChatMessages=None, convert_output_to_messages=None) and verify: module loads, content read directly, empty content returns empty without DB attempt, full action flow preserves id invariant. Low-version users should install v1.0.1 (last pre-fix release): https://github.com/Fu-Jie/openwebui-extensions/releases/tag/smart-mind-map-v1.0.1 Note: the guarded imports still let v1.0.3 *run* on older OWUI, but `required_open_webui_version` makes the supported boundary explicit and prevents silent partial functionality (recovery falls back to empty on pre-v0.10 when content is empty).
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
Update the Smart Mind Map version badge in docs/plugins/actions/index.md and index.zh.md from 1.0.1 to 1.0.3 so all 7 version locations stay consistent (release-prep skill check now passes).
…ersion locations v1.0.3 was never tagged/released, so collapse both layers of the OWUI v0.10+ fix (frontend crash + empty content recovery) into a single v1.0.2 release. Syncs .py docstring/log strings, README/README_CN, docs, index, and renames v1.0.3(.md|_CN.md) -> v1.0.2(.md|_CN.md). Version consistency check passes; 26 regression tests still green.
Closes #101
Problem
On OpenWebUI v0.10+, clicking the Smart Mind Map action icon crashes the frontend:
The Docker log shows the action itself returns HTTP 200 in ~38 ms (no LLM call), yet the browser crashes.
Root cause (confirmed against OWUI main source)
I fetched the current OWUI
mainsource to trace the exact failure path instead of guessing.Frontend (
Chat.svelte→chatActionHandler, Chat.svelte:1791-1802):The frontend looks up every returned message by
idinhistory.messages. Ifhistory.messages[message.id]isundefined, accessing.contentthrows.Plugin (before this fix) — all three early-return paths appended a new message dict without an
id:When the frontend iterates that appended message,
message.idisundefined,history.messages[undefined]isundefined, and the crash follows. This is exactly the 38 ms / HTTP 200 / no-LLM-call scenario in the issue: an early-return path was hit (empty/short content), the plugin appended an id-less message, and the frontend crashed while processing the otherwise-successful response.Fix
Update the existing last message's content (it already carries a valid
id) instead of appending a new id-less message. The OWUI frontend preserves the original content asoriginalContentbefore applying the update, so nothing is lost. The error is still surfaced to the user via the notification emitter (_emit_notification)._set_last_message_content(body, content)helper with safe fallbacks (no-op whenbodyhas no messages, handles non-dict last entry, preservesidand other fields).bodyunchanged (cannot safely touchbody["messages"]).1.0.1→1.0.2.Tests
New regression test suite (
tests/plugins/actions/smart-mind-map/test_issue_101_regression.py, 11 cases). Verified the tests fail on the old code (11 failures, includingassert 3 == 2showing the extra appended message, andMessage without id would crash OWUI v0.10 frontend) and pass on the fix.Key invariant enforced: every message in the returned
body["messages"]must carry anid.Issue #97 regression tests still pass (no break to markdown_normalizer):
Notes for the issue reporter
error_no_contentnotification — it means the last assistant message had no extractable text content at the moment the action ran. After this fix the crash is gone; if you still see the notification (without a crash), that's a separate content-availability question (e.g. the model response being too short, or stored in a structured-output format). Please retry on v1.0.2 and report back.