You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS中IPInfo.io请求异常逃逸try-catch致服务器崩溃排查

NodeJS服务器中IPInfo.io调用错误逃逸try-catch导致崩溃的问题

我搭建了一个NodeJS服务器,用IPInfo.io查询访客所属国家,但偶尔会因为IPInfo.io的响应错误导致服务器崩溃,错误信息如下:

SyntaxError: Unexpected token o in JSON at position 1
    at JSON.parse (<anonymous>)
    at IncomingMessage.<anonymous> (~/server/node_modules/node-ipinfo/dist/src/ipinfoWrapper.js:93:43)

这是我唯一使用IPInfo的代码:

const IPinfo = require('node-ipinfo');

async function getVisitor(req) {
      // ....
      console.log(req.originalUrl, " :: ", req.ip, " :: ", ua);

      let ip = req.ip;
      if (ip.startsWith("::ffff:")) {
        ip = ip.replace(new RegExp("^::ffff:"), '');
      }

      console.log("Requesting IP information for " + ip);
      let countryCode;
      let ipinfo = new IPinfo(config.ipinfo);
      try {
        let info = await ipinfo.lookupIp(ip);
        countryCode = info.countryCode;
      } catch (e) {
        console.error("\n# IP Info error for ip address: " + ip + " (" + req.ip + ")")
        countryCode = undefined;
      }
}

我搞不懂为什么这个错误能逃出try-catch块,大部分时候代码运行正常,但偶尔触发错误就会导致服务器重启,相关错误日志和输出日志如下:

错误日志:

[2024-06-18T03:08:59.465Z] SyntaxError: Unexpected token o in JSON at position 1
    at JSON.parse (<anonymous>)
    at IncomingMessage.<anonymous> (~/server/node_modules/node-ipinfo/dist/src/ipinfoWrapper.js:93:43)
    at IncomingMessage.emit (node:events:513:28)
    at IncomingMessage.emit (node:domain:489:12)
    at emitCloseNT (node:internal/streams/destroy:138:10)
    at processTicksAndRejections (node:internal/process/task_queues:82:21)
[2024-06-18T03:09:03.510Z] 
# HTTPS server started

输出日志:

[2024-06-18T03:08:59.222Z] /  ::  147.185.132.177  ::  Expanse, a Palo Alto Networks company, searches across the global IPv4 space multiple times per day to identify customers&#39; presences on the Internet. If you would like to be excluded from our scans, please send IP addresses/domains to: scaninfo@paloaltonetworks.com
[2024-06-18T03:08:59.228Z] Requesting IP information for 147.185.132.177
[2024-06-18T03:09:03.341Z] 
# server starting...

我查过相关资料,但觉得我的情况不一样,因为我用了await来请求IPInfo。想问为什么错误会逃逸try-catch块,或者该怎么追踪错误来源?


问题原因分析

  • 这个错误并非来自lookupIp的Promise拒绝,而是来自IncomingMessage的close事件回调里的JSON解析错误。node-ipinfo库内部处理响应流时,没有正确捕获解析错误,导致错误作为未捕获的事件抛出,跳出了你的try-catch范围。
  • 当响应流关闭时触发的回调执行JSON.parse,如果返回的内容不是合法JSON(比如已被解析过一次,或者返回了非JSON格式的错误内容),就会抛出SyntaxError,而这个回调没有被包裹在Promise的错误处理链里,所以你的try-catch抓不到。

解决方案

1. 全局捕获未处理的异常

在服务器启动代码里添加全局错误捕获,避免服务器直接崩溃:

// 捕获未处理的Promise拒绝
process.on('unhandledRejection', (reason, promise) => {
  console.error('未处理的Promise拒绝:', reason, promise);
});

// 捕获未捕获的异常
process.on('uncaughtException', (err) => {
  console.error('未捕获的异常:', err);
  // 可选:这里可以添加优雅重启服务器的逻辑
});

2. 排查或更换IPInfo库

  • 检查node-ipinfo的最新版本是否修复了该bug,若未修复,可在其仓库提交issue说明错误场景和日志。
  • 若库的问题无法及时解决,可放弃使用该库,手动用fetch或axios调用IPInfo API,自己控制响应解析和错误捕获:
async function getVisitor(req) {
  // ... 其他代码
  let countryCode;
  try {
    const response = await fetch(`https://ipinfo.io/${ip}/json?token=${config.ipinfo}`);
    if (!response.ok) {
      throw new Error(`HTTP请求失败,状态码: ${response.status}`);
    }
    const info = await response.json();
    countryCode = info.country;
  } catch (e) {
    console.error("\n# IP Info查询错误,IP地址: " + ip + " (" + req.ip + ")", e);
    countryCode = undefined;
  }
}

3. 临时规避:增加响应格式校验

如果坚持使用原库,可以尝试在调用前增加IP合法性校验,避免向IPInfo发送无效IP导致异常响应:

// 简单的IP格式校验函数
function isValidIp(ip) {
  return /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ip);
}

async function getVisitor(req) {
  // ... 其他代码
  if (!isValidIp(ip)) {
    console.warn("无效IP地址,跳过IPInfo查询:", ip);
    countryCode = undefined;
    return;
  }
  // 后续IPInfo调用逻辑
}

内容的提问来源于stack exchange,提问作者ginjaemocoes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 08:44:53