打印 URL 子类原型报 Receiver must be an instance of class URL
日志格式化遍历到 URL 子类原型时,可能触发 href 接收者错误。区分真实 URL 实例与原型,缩小日志字段,并确认修复是否进入正式发行。
一次 console.log() 抛出 TypeError: Receiver must be an instance of class URL,栈却落在 node:internal/util/inspect,不一定是创建 URL 时传错了字符串。检查打印对象中是否包含 URL 子类的原型,或使用了会展开隐藏属性的 %o、showHidden: true。
最小触发结构很短:
class MyURL extends URL {}
console.log(MyURL.prototype);
截至2026年10月20日,这条路径已在 Node.js 24.21.0、26.12.0得到复现确认。24.22.0正式源码中也保留了相同的 isURL() 判断。其他版本应按实际版本检查,不能把任意包含 href 的对象报错都归到这里。
看的是格式化调用,不是 URL 输入
console.log()、util.inspect() 会遍历和格式化对象。开启隐藏属性显示时,打印类本身或包含该类的更大对象,也可能走到它的原型。因此,DOM模拟环境里打印整个 window,错误可以由很深的一层 URL 子类触发,业务代码未必直接读取过 href。
先保留完整栈,确认出现 get href、isURL、formatRaw 等内部格式化入口,再看错误前最后一条日志实际传了什么。若是业务直接把 URL.prototype.href 当成实例属性访问,属于另一种接收者误用,不能靠更换日志格式解释掉。
受影响判断会先读取对象的 href。URL子类的原型通过原型链看起来属于 URL,却没有真正 URL 实例所需的内部状态,读取 getter 时因此抛错。单纯把 instanceof URL 放在读取前面也不够,它仍无法区分这种原型与完整实例。
缩小日志对象,保留能诊断业务的信息
如果只是想记录所持对象的类型,不必展开原型或整个运行环境。对于已经掌握变量来源的代码,可以记录明确的标量:
class MyURL extends URL {}
const pageURL = new MyURL('https://example.org/path');
console.log({
urlClass: MyURL.name,
pageAddress: pageURL.href,
});
这里 pageURL 是实际构造的实例,示例地址不承载真实请求。实际应用还需去掉地址中的令牌或敏感查询参数。不要用这个片段遍历任意未知对象后自动读取所有 href;未知 getter 仍可能有副作用或主动抛错。
若原日志是 console.log('%o', environment),先改为需要的状态字段,避免递归展开含类和原型的大对象。关闭 showHidden 可能避开某些间接路径,但直接打印 MyURL.prototype 仍会触发,不能当作完整修复。
customInspect: false 也不是可靠开关:已合并的回归检查同时覆盖开启和关闭自定义格式化的情况。不要为排一条日志而改全局 URL 原型、给 getter 伪造返回值,或覆盖整个 console;这些做法会改变其他代码依赖的运行时行为。
修复版要核对发行内容
2026年10月20日合入的修复改用内部实例检查,先确认对象具备 URL 所需状态,再访问 href;相关检查同时覆盖子类原型、隐藏属性、%o 和正常实例。这个合并晚于已核对的24.22.0、26.12.0正式发布时间,不能据此写成这两个版本已经包含修复。
选用后续正式版本前,确认发行内容包含该改动。恢复原日志时,在允许的诊断环境检查直接原型打印、原来失败的较大对象,以及普通URL实例显示;同时确认日志没有意外输出私密状态。暂时保留字段化日志通常也更容易定位业务问题,不必为了验证修复重新输出整个环境对象。


