Skip to content

fix: DOMException 错误信息丢失 —— String(e) 输出 [object DOMException] 而非真实错误消息 #54

Description

@Teeeeeeeerry

现象

https://github.com/torvalds/uemacs 整页翻译时:

[PT] 翻译失败: [object DOMException]

DOMException 的真实 message(name + 描述)被 String(e) 吞掉,无法定位根因。

根因

entrypoints/content.ts:282-288 (togglePage() 的 catch 块):

} catch (e) {
    console.error('[PT] 翻译失败:', e);       // 原始对象,部分日志系统只展示 [object DOMException]
    if (isMainFrame) {
        toast(String(e), 'error');            // ← String(e) 在特定环境下产生 "[object DOMException]"
        setBallState('error');
    }
    return 'error';
}

DOMException 继承自 Error.name.message 均已有值(如 HierarchyRequestError: Failed to execute 'appendChild'...),但 String(e) 在特定 console 实现中走的是 Object.prototype.toString 路径,输出 [object DOMException] 而非 .message

应改为 e instanceof Error ? e.message : String(e)

同类问题还存在于

文件 行号 上下文
entrypoints/content.ts 374, 376 popup reply String(e)
entrypoints/background.ts 79, 81 sendResponse 错误路径
src/engines/router.ts 139 new EngineError(id, true, String(e))

可能的 DOMException 来源(待确认)

由于真实 name/message 被吞,只能根据 GitHub 页面特征推测。异常必须由 doTranslate() 内部抛出(该函数是 togglePage 中唯一被 await 的调用):

1. document.createTreeWalker()walker.ts:33

根据 DOM 规范,acceptNode 过滤器内抛出的任何异常都会经 walker.nextNode() 向外传播。该过滤器对页面每个元素运行,是覆盖面最大的抛异常面:

  • applyCompat(el) → github.com 分支 (compat.ts:42-78) 调用 el.closest(...)
  • shouldSkipNonVisual(el)el.closest(...) (classify.ts:67,70,73) + el.textContent / el.outerHTML (classify.ts:75,77)
  • hasNonTextContent(el)el.querySelector(NON_TEXT_SELECTOR) (classify.ts:112)
  • walk(el.shadowRoot, ...) → 递归 shadow root(walker.ts 注释已记录此处历史 TypeError,是已知脆弱路径)

2. render()renderer.ts:43

while (el.firstChild) origin.appendChild(el.firstChild);

appendChild 在层级冲突时抛出 HierarchyRequestError。GitHub 是 React SPA,页面可能在 collect() 采集 → await sendMessagerender() 之间已将被采集元素从 DOM 中移除或替换。

3. chrome.runtime.sendMessage()content.ts:173

结构化克隆失败时抛出 DataCloneError(当前 payload 均为字符串/简单对象,不应触发;但扩展升级/重载导致的 context invalidated 也会走此 reject 路径)。

修复方案

1. 正确提取 DOMException 的 message(必须)

- toast(String(e), 'error');
+ const msg = e instanceof Error ? e.message : String(e);
+ toast(msg, 'error');

同步修复 console.error 及其他 4 处 String(e) 站点。

2. 为 shouldSkipNonVisual 增加防御(推荐)

GitHub 大量 Web Components(<relative-time><clipboard-copy><include-fragment> 等)上访问 textContent / outerHTML 的 getter 可能抛出。用 try-catch 包裹外层,失败时安全跳过(宁可漏翻单个可疑节点,也不要整页崩溃)。

3. 为 doTranslate 增加阶段标记(推荐)

在错误中携带当前操作上下文(collect / batch N / render),即使 .message 不完整也能缩小排查范围。

影响范围

  • 任何 DOMException 场景(不限于 GitHub)都会因 String(e) 输出无意义字符串
  • 用户看到 toast 显示 [object DOMException],无法采取任何行动
  • 开发者无法从日志定位具体 DOM 操作失败点 —— 修复 1 是定位真正根因的前提条件

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions