修复 PNPM 与 Node.js 20 的兼容性问题 (ERR_INVALID_THIS)

2 min read

问题

在 Node.js (>= 20.X) 中,PNPM (< 8.3.1) 可能会抛出 ERR_INVALID_THIS 错误。

Lockfile is up to date, resolution step is skipped
Progress: resolved 1, reused 0, downloaded 0, added 0
.                                  | +983 ++++++++++++++++++++++++++++++++
 WARN  GET xxxxx error (ERR_INVALID_THIS). Will retry in 10 seconds. 2 retries left.
 WARN  GET xxxxx error (ERR_INVALID_THIS). Will retry in 10 seconds. 2 retries left.
 WARN  GET xxxxx error (ERR_INVALID_THIS). Will retry in 10 seconds. 2 retries left.
 WARN  GET xxxxx error (ERR_INVALID_THIS). Will retry in 10 seconds. 2 retries left.
// ...
Error: Process completed with exit code 1.

解决方案

升级 PNPM 到 8.3.1 或更高版本。

pnpm install -g pnpm@latest

问题原因

这个问题的根本原因在于 PNPM 模块依赖的 node-fetch 库中,URLSearchParams.prototype[p].call() 函数调用时,错误地把 this 上下文设置为了 receiver(代理对象)而非 target(原始对象)。将 this 上下文设置为 receiver 导致 URLSearchParams 的方法无法正确访问原始对象的属性和方法,进而抛出 ERR_INVALID_THIS 异常。

具体可以参考 pnpm/pnpm#6424pnpm/node-fetch@ebe57b

// @@ -114,7 +114,7 @@
// export default class Headers extends URLSearchParams {
validateHeaderName(name);
validateHeaderValue(name, String(value));
return URLSearchParams.prototype[p].call(
  receiver, 
  target, 
  String(name).toLowerCase(),
  String(value),
);
// @@ -126,7 +126,7 @@
// export default class Headers extends URLSearchParams {
return (name) => {
  validateHeaderName(name);
  return URLSearchParams.prototype[p].call(
    receiver, 
    target, 
    String(name).toLowerCase(),
  );
};

通过将 this 上下文正确地设置为 target(原始对象),URLSearchParams 的方法可以正确访问原始对象的属性和方法,从而解决了 ERR_INVALID_THIS 异常。

总结

如果你在使用 Node.js 20 和 PNPM 是遇到了 ERR_INVALID_THIS 异常,请升级到 PNPM v8.3.1 或更高版本。

⚠️ 注意: Node.js 20.0.0 版本存在异常断流问题,建议升级到 Node.js 20.1.0 或更高版本。

Previous article