完全披露:我在撰写此错误报告前,曾通过 ask.discourse 进行过相关讨论。另外,我知道我的版本更新滞后了,但我希望趁记忆还清晰时发布此问题。
我已在两个不同的用户账户上复现了该问题。
摘要
对于已书签(Bookmark)Data Explorer 组报告的用户,请求用户数据存档(偏好设置 → 导出您的数据 → 请求存档)会失败。Jobs::ExportUserArchive 后台作业在生成书签 CSV 时崩溃,导致导出无法完成,且未发送下载链接。
环境信息
- Discourse 版本:2026.8.0-latest.1
- Data Explorer:随核心捆绑(版本跟随核心)
- 自托管 / 托管:自托管
- 根据回溯显示的 Ruby/Rails 技术栈:activemodel/activerecord 8.0.5.1, Sidekiq 7.3.10
复现步骤
- 以管理员身份创建一个 Data Explorer 查询,并授予某个组的访问权限。
- 在该组的 Reports(报告)选项卡中,点击查询旁的 Bookmark(书签)按钮,使其成为已书签的组报告。
- 使用该同一用户,前往 偏好设置 → 导出您的数据 → 请求存档。
- 观察作业失败情况(检查
Admin → Logs或 Sidekiq 日志)。
预期行为
应生成用户存档,并且用户应收到包含其书签在内的下载链接。
实际行为
导出作业抛出异常,导致存档失败。回溯显示崩溃发生在 bookmarks_export 中,该作业对已书签对象调用了 .url:
Job exception: undefined method 'url' for an instance of DiscourseDataExplorer::QueryGroup
回溯(仅保留相关帧):
activemodel-8.0.5.1/lib/active_model/attribute_methods.rb:512:in 'ActiveModel::AttributeMethods#method_missing'
activerecord-8.0.5.1/lib/active_record/attribute_methods.rb:495:in 'ActiveRecord::AttributeMethods#method_missing'
/var/www/discourse/app/jobs/regular/export_user_archive.rb:325:in 'block in Jobs::ExportUserArchive#bookmarks_export'
activerecord-8.0.5.1/lib/active_record/relation/delegation.rb:101:in 'Array#each'
activerecord-8.0.5.1/lib/active_record/relation/delegation.rb:101:in 'ActiveRecord::Delegation#each'
/var/www/discourse/app/jobs/regular/export_user_archive.rb:319:in 'Jobs::ExportUserArchive#bookmarks_export'
/var/www/discourse/app/jobs/regular/export_user_archive.rb:170:in 'Kernel#public_send'
/var/www/discourse/app/jobs/regular/export_user_archive.rb:170:in 'block (2 levels) in Jobs::ExportUserArchive#execute'
/var/www/discourse/app/jobs/regular/export_user_archive.rb:168:in 'block in Jobs::ExportUserArchive#execute'
分析
书签是多态的。导出作业调用 bookmarkable.url,这对核心可书签类型(Topic、Post)有效,但 DiscourseDataExplorer::QueryGroup 未实现 url 方法,因此作业在处理第一个此类书签时会引发 NoMethodError。
看起来 Data Explorer 将查询组注册为可书签对象(通过组报告的 Bookmark 按钮),但该模型缺少 url 方法(或者核心导出作业需要针对未提供 url 方法的书签类型添加保护机制)。
建议修复方案
要么在 DiscourseDataExplorer::QueryGroup 上实现 url 方法,要么使核心的 bookmarks_export 更具防御性(例如使用 bookmarkable.respond_to?(:url) / 逐行 rescue),以便单个不受支持的书签类型不会导致整个导出失败。
临时解决方案
删除有问题的书签(即受影响用户的 bookmarks 表中 bookmarkable_type = 'DiscourseDataExplorer::QueryGroup' 的行)可使导出正常完成。