头像选择器模态框因“t.split 不是函数”崩溃 (avatar-selector.gjs)

摘要:
打开头像选择器模态框(用于更改个人资料头像)时抛出 JavaScript 错误,且弹出窗口无法正常工作。

Discourse 版本: v2026.7.0-latest (commit 59588b5d2a)
Ember 版本: v6.10.1

复现步骤:

  1. 进入用户个人资料
  2. 点击更改头像选项
  3. 头像选择器模态框打开(根据堆栈跟踪,由“已解决”移动确认模态框流程触发)
  4. 浏览器控制台立即出现错误,且模态框行为异常

预期行为:
头像选择器应正常打开,并允许用户选择/上传头像。

实际行为:
控制台抛出:TypeError: t.split is not a function,位于 avatar-selector.gjs 中的 selectableAvatars getter 内。

已进行的故障排除:

  • 在禁用所有主题和插件的安全模式下测试,错误仍然存在,因此似乎不是由主题或插件引起的。

浏览器 / 操作系统:

Brave / Windows 11

控制台日志 / 堆栈跟踪:
```
?safe_mode=no_themes,no_plugins:346 GET https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496 net::ERR_BLOCKED_BY_CLIENT
app.js:365 :information_source: Discourse v2026.7.0-latest — Commits · discourse/discourse · GitHub — Ember v6.10.1
avatar-selector.gjs:47 Uncaught (in promise) TypeError: t.split is not a function
at get selectableAvatars (avatar-selector.gjs:47:47)
at R (cache-DtDz7X5V.js:1647:15)
at index.js:87:32
at index.js:64:37
at zn (index.js:396:5)
at j (index.js:62:16)
at index-DaCZcoCY.js:4335:20
at index.js:64:37
at zn (index.js:396:5)
at j (index.js:62:16)
at index-DaCZcoCY.js:4335:20
at index.js:64:37
at zn (index.js:396:5)
at j (index.js:62:16)
at index.js:158:20
at index.js:64:37
at zn (index.js:396:5)
at j (index.js:62:16)
at Object.evaluate (index.js:1970:16)
at Object.evaluate (index.js:103:106)
at zb.evaluateSyscall (index.js:2873:20)
at zb.evaluateInner (index.js:2852:64)
at zb.evaluateOuter (index.js:2849:10)
at e.next (index.js:4167:45)
at e._execute (index.js:4157:21)
at e.execute (index.js:4133:41)
at _x.handleException (index.js:3450:19)
at bx.handleException (index.js:3592:52)
at hx.throw (index.js:3414:16)
at iy.evaluate (index.js:565:37)
at hx._execute (index.js:3401:34)
at hx.execute (index.js:3393:17)
at xx.rerender (index.js:3610:8)
at ww.render (index-DaCZcoCY.js:5211:23)
at index-DaCZcoCY.js:5409:16
at xb (index.js:2414:7)
at e.renderRoots (index-DaCZcoCY.js:5389:7)
at #o (index-DaCZcoCY.js:5374:12)
at e.revalidate (index-DaCZcoCY.js:5435:10)
at invoke (index.js:264:14)
at Rc.flush (index.js:180:11)
at zc.flush (index.js:334:19)
at pl._end (index.js:762:32)
at _boundAutorunEnd (index.js:499:12)
selectableAvatars @ avatar-selector.gjs:47
_getProp @ cache-DtDz7X5V.js:1647
(anonymous) @ index.js:87
(anonymous) @ index.js:64
track @ index.js:396
valueForRef @ index.js:62
(anonymous) @ index-DaCZcoCY.js:4335
(anonymous) @ index.js:64
track @ index.js:396
valueForRef @ index.js:62
(anonymous) @ index-DaCZcoCY.js:4335
(anonymous) @ index.js:64
track @ index.js:396
valueForRef @ index.js:62
(anonymous) @ index.js:158
(anonymous) @ index.js:64
track @ index.js:396
valueForRef @ index.js:62
(anonymous) @ index.js:1970
evaluate @ index.js:103
evaluateSyscall @ index.js:2873
evaluateInner @ index.js:2852
evaluateOuter @ index.js:2849
next @ index.js:4167
_execute @ index.js:4157
execute @ index.js:4133
handleException @ index.js:3450
handleException @ index.js:3592
throw @ index.js:3414
evaluate @ index.js:565
_execute @ index.js:3401
execute @ index.js:3393
rerender @ index.js:3610
(anonymous) @ index-DaCZcoCY.js:5211
(anonymous) @ index-DaCZcoCY.js:5409
inTransaction @ index.js:2414
renderRoots @ index-DaCZcoCY.js:5389
#o @ index-DaCZcoCY.js:5374
revalidate @ index-DaCZcoCY.js:5435
invoke @ index.js:264
flush @ index.js:180
flush @ index.js:334
_end @ index.js:762
(anonymous) @ index.js:499
Promise.then
(anonymous) @ index.js:18
flush @ index.js:29
_scheduleAutorun @ index.js:928
_end @ index.js:768
(anonymous) @ index.js:499
Promise.then
(anonymous) @ index.js:18
flush @ index.js:29
_scheduleAutorun @ index.js:928
_end @ index.js:768
(anonymous) @ index.js:499
Promise.then
(anonymous) @ index.js:18
flush @ index.js:29
_scheduleAutorun @ index.js:928
_ensureInstance @ index.js:919
ensureInstance @ index.js:731
scheduleRevalidate @ index-DaCZcoCY.js:4146
dirtyTag @ index.js:229
dirtyTagFor @ index.js:848
setter @ index.js:871
set @ cache-DtDz7X5V.js:1886
show @ modal.js:57
await in show
showAvatarSelector @ account.js:43
_join @ index.js:788
join @ index.js:605
join @ index.js:152
(anonymous) @ route-action.js:48
(anonymous) @ index.js:2534
(anonymous) @ d-button.gts:216
invoke @ index.js:264
flush @ index.js:180
flush @ index.js:334
_end @ index.js:762
end @ index.js:565
_runExpiredTimers @ index.js:869
setTimeout
setTimeout @ index.js:39
_installTimerTimeout @ index.js:912
_reinstallTimerTimeout @ index.js:896
_later @ index.js:829
later @ index.js:652
next @ index.js:562
_triggerAction @ d-button.gts:213
click @ d-button.gts:167
```

2 个赞

你能在 meta 上复现这个问题吗?

上面的错误信息是否来自你的实例?你是否使用了 Cloudflare?这一部分似乎表明是这样。

1 个赞

你好,

感谢你的回复以及指出这一点。

回答你的问题:不,我无法在 meta.discourse.org 本身复现此问题,仅在我自己的论坛中出现。

关于 Cloudflare:是的,我的论坛确实位于 Cloudflare 之后,但那条特定的控制台日志(beacon.min.js / ERR_BLOCKED_BY_CLIENT)与实际 bug 无关。它是由 Brave Shields 阻止 Cloudflare 的分析脚本引起的。我完全禁用了 Brave Shields,该行日志便消失了,但头像选择器错误(TypeError: t.split is not a function)仍然以完全相同的方式发生。

作为背景信息:我的论坛在 Cloudflare 之后运行了将近 3 年,直到最近,更改个人资料头像一直都能正常工作。因此,这似乎是一个回归问题,而非长期存在的与 Cloudflare 相关的问题。

由于该问题无法在 meta 上复现,这可能是我配置中的特定问题所致。我很乐意深入排查任何有助于缩小范围设置或日志。请告诉我哪些内容值得检查(例如,“可选头像”站点设置的值,或与 Solved 插件相关的内容,我注意到该插件在堆栈跟踪中作为触发此模态窗口的原因出现)。

感谢你的调查。

2 个赞

是的,你说得对,很可能不是 Cloudflare。

我在一个单独的实例上进行了测试,在那里我只启用了可选头像。我不得不上传两个头像(站点设置要求),然后启用。

你能修改这两个设置,看看你的那边是否有任何变化吗?

2 个赞

你好,

感谢对此进行深入调查。你是对的。

按照你的建议,我在“可选头像”设置中上传了两个头像,错误已消失。头像选择器现在可以正常打开并运行。

因此,根本原因似乎是:当启用“可选头像”功能但上传的头像数量少于所需数量(或为空)时,模态框会崩溃,而不是优雅地处理失败。在我的案例中,该设置已启用但未上传任何头像,从而触发了 t.split is not a function 错误。

我仍然认为这需要在核心层面进行修复。即使设置配置不当,模态框也不应抛出未捕获的 TypeError 并完全破坏头像选择功能。理想情况下,它应该:

  • 如果列表为空或无效,则回退到正常的头像选择(跳过“可选头像”功能),或者
  • 显示清晰的验证错误,而不是静默的 JS 崩溃

再次感谢帮助缩小问题范围!

2 个赞

是的,这很公平。你想提交一个修复吗?如果你熟悉的话,我很乐意接受这个的 Pull Request。

1 个赞

我从事软件开发,但对这类事情没有太多经验。所以我有点犹豫是否自己提交修复,担心会出错。如果核心团队的有人愿意接手,那就太好了。如果你需要,我可以在 GitHub 上提一个 issue。

3 个赞

没问题,这应该能解决问题:DEV: Fix JS error in modal when selected avatars is misconfigured - Pull Request #41663 - discourse/discourse - GitHub

1 个赞