issue: note edits are lost when an update without a content snapshot cancels the pending save

该报错发生在 Open WebUI 的笔记(Notes)协作编辑场景中。当服务端 Yjs 状态为空(例如重启后或笔记由非协作方式创建)但数据库中已有内容时,一次不含 data 的更新会取消待执行的保存任务却没有重新调度保存,导致编辑内容丢失。优先检查 Open WebUI 是否使用了 Redis 作

快速结论:该报错发生在 Open WebUI 的笔记(Notes)协作编辑场景中。当服务端 Yjs 状态为空(例如重启后或笔记由非协作方式创建)但数据库中已有内容时,一次不含 data 的更新会取消待执行的保存任务却没有重新调度保存,导致编辑内容丢失。优先检查 Open WebUI 是否使用了 Redis 作为 WebSocket 管理器,以及服务端 Yjs 内存状态是否被重启清空。

适用环境:Open WebUI v0.11.0(Docker Compose 部署,Postgres 16 后端,未配置 Redis,WEBSOCKET_MANAGER 未设置,ENABLE_WEBSOCKET_SUPPORT 使用默认值)。Issue 作者在 Debian 12 宿主机上通过 Docker Compose 复现。

最快修复方案:暂无已确认的一步修复方案。Issue 中提到了触发条件,但未给出已验证的代码修复或配置变更。可优先尝试为 Open WebUI 配置 Redis,将 Yjs 更新日志从进程内存迁移到独立存储,避免服务重启导致 Yjs 历史丢失。

注意事项:该方案仅为推测,未被 Issue 作者验证。此外,即使配置 Redis,对于“笔记由非协作方式创建(如 write_note 工具或直接 POST)后首次打开”的场景,仍可能存在 Yjs 状态为空导致保存丢失的问题,需要配合其他修复措施。

问题场景

用户在使用 Open WebUI 的笔记编辑器时,输入的文字不会保存到数据库。编辑器界面始终显示完整文本,但数据库中的 note.data.content 仍是旧版本。只有继续输入一个字符,之前所有文字才会被写入数据库。等待、离开输入框或切换页面均无法触发保存。该问题在 Open WebUI v0.11.0 版本中复现。

报错原文

issue: note edits are lost when an update without a content snapshot cancels the pending save

该问题不产生具体的错误日志——保存操作从未被尝试执行,因此没有报错信息。Issue 作者明确指出“That silence is part of the problem”(这种静默本身就是问题的一部分)。

原因分析

问题出在 backend/open_webui/socket/main.pyyjs_document_update 函数中。待保存任务的取消和新任务的调度处于不同的条件分支下:

  • stop_item_tasks(REDIS, document_id) 始终执行,用于取消待保存任务;
  • create_task(REDIS, debounced_save(), document_id) 仅在 data.get('data') 为真时才会执行。

当一次更新不携带 data 字段时(即“无内容快照”的更新),待保存任务被取消,却没有新的保存任务顶替,导致本次编辑内容永远不会写入数据库。

向服务器发送无 data 更新的代码路径来自 SocketIOCollaborationProvidersrc/lib/components/common/RichTextInput/Collaboration.ts)中的 ydoc:document:state 处理器:当服务端报告 Yjs 文档状态为空、但编辑器已持有内容时,它会发出只含 update 字段(无 data)的 ydoc:document:update 消息。

这意味着以下场景容易触发:

  1. 非协作方式创建的笔记:write_note 内置工具或直接调用 POST /api/v1/notes/create 创建的笔记没有任何 Yjs 历史记录(相关问题见 #28539)。
  2. 服务重启后:未配置 Redis 时,YdocManager 将更新日志保存在进程内存(self._updates = {})。每次重启 Open WebUI,所有笔记的 Yjs 历史都会丢失,而 Postgres 中的 note.data.content 仍然存在。下次打开任何有内容的笔记时,服务端会报告空状态,触发上述无 data 更新分支。

此外,NoteEditor.svelte 没有在卸载时(onDestroy)执行保存操作,其 REST 防抖保存仅提交 titledata.filesaccess_grants,从不提交 data.content。Yjs 路径是笔记文本的唯一写入通道,因此一旦保存被取消且无替补,数据就会丢失。

环境排查

  • 确认 Open WebUI 版本是否为 v0.11.0 或更新(Issue 作者确认 dev 分支代码路径未变)。
  • 检查是否配置了 Redis(WEBSOCKET_MANAGER 环境变量)。未配置时 REDISNone,Yjs 更新日志仅存于进程内存。
  • 确认 Postgres 版本(Issue 中为 16)。
  • 确认部署方式是否为 Docker Compose 及镜像标签(ghcr.io/open-webui/open-webui:v0.11.0)。
  • 检查笔记是否由协作编辑器之外的方式创建(如 write_note 工具、API 调用),此类笔记没有 Yjs 历史。
  • 确认 Open WebUI 最近是否发生过重启(导致内存中 Yjs 历史被清空)。

解决步骤

  1. 首选为 Open WebUI 配置 Redis(设置 WEBSOCKET_MANAGER 环境变量),使 YdocManager 的更新日志不再依赖进程内存,从而避免服务重启导致全部 Yjs 历史丢失。这是针对“重启导致空状态”触发条件的可能解决方案。
  2. 如配置 Redis 后问题仍存在,或笔记由非协作方式创建,需要从代码层面修复 yjs_document_update 的逻辑——将保存任务调度移出 data.get('data') 条件,确保即使更新不带 data,也会重新调度一个保存任务以承接被取消的任务。
  3. 作为临时规避手段(未经 Issue 验证):在每次输入后,通过 UI 强制触发一次带 data 的保存,或在编辑后手动创建一个新的更新来触发保存链路。
  4. 跟踪相关 Issue(#28539#26140#25916)或上游修复进展,等待官方补丁。

验证方法

按以下步骤确认问题是否复现或已解决:

  • 创建一个新的笔记,输入一段文字,等待防抖保存完成后离开笔记页面。
  • 重启 Open WebUI(或模拟服务端 Yjs 状态清空),重新打开该笔记,确认编辑器中的内容与数据库一致。
  • 在服务重启后立即编辑该笔记(不输入新字符),等待数秒后检查数据库中 note.data.content 是否仍为旧内容——若为旧内容则问题复现。
  • 若已应用修复,在同样操作下,编辑后的文字应自动写入数据库,无需额外输入字符触发。

参考来源

open-webui/open-webui #28667

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 20090

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注