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

Node.js突发Warning: Detected unsettled top-level await问题求助

问题

代码突然抛出警告Warning: Detected unsettled top-level await,进程输出警告后直接退出。此前一小时运行完全正常,且未对开发环境做任何修改。怀疑是动态导入Promise或startTunnel Promise未settle导致,但不解为何突然出现此问题。

相关代码

let appUrl: string | undefined = isProd
  ? process.env.APP_URL
  : await (async () => {
      const { default: startTunnel } = await import("./lib/startTunnel.js");
      const url = await startTunnel(PORT);
      return url;
    })();

环境信息

  • Node.js v22.4.1
  • TypeScript v5.5.4

tsconfig.json 配置

// https://www.totaltypescript.com/tsconfig-cheat-sheet
{
  "compilerOptions": {
    /* Base Options: */
    "esModuleInterop": true,
    "skipLibCheck": true,
    "target": "es2022",
    "allowJs": true,
    "resolveJsonModule": true,
    "moduleDetection": "force",
    "isolatedModules": true,

    /* Strictness */
    "strict": true,
    "noUncheckedIndexedAccess": true,

    /* If transpiling with TypeScript: */
    "module": "NodeNext",
    "outDir": "dist",
    "sourceMap": true,

    /* If your code doesn't run in the DOM: */
    "lib": ["es2022"]
  }
}

startTunnel.ts 代码

import { startTunnel } from "untun";


export default async (port: number) => {
  try {
    const tunnel = await startTunnel({ port });

    if (!tunnel) throw new Error("unable to start tunnel");

    const tunnelUrl = await tunnel.getURL();

    return tunnelUrl;
  } catch (error) {
    console.error((error as Error).message);
  }
};

分析与解决

核心原因

  1. startTunnel异常处理不完整:当隧道初始化或获取URL失败时,catch块仅打印错误但未返回值或重新抛出异常,导致该async函数返回一个状态为fulfilled但值为undefined的Promise,或因未处理的深层异常导致Promise悬而未决。
  2. 外部服务偶发故障:untun依赖的隧道服务可能因网络波动、第三方服务限流/故障等外部因素,突然出现初始化失败或超时,触发异常分支。
  3. Node.js v22的严格检测:Node.js v22对顶层await的Promise状态检测更严格,一旦检测到顶层Promise长时间未settle或存在未处理的异常,就会抛出警告并终止进程。

修复方案

1. 完善startTunnel的异常处理

确保异常分支要么明确返回 fallback 值,要么重新抛出错误,避免Promise状态异常:

import { startTunnel } from "untun";

export default async (port: number) => {
  try {
    const tunnel = await startTunnel({ port });

    if (!tunnel) throw new Error("unable to start tunnel");

    const tunnelUrl = await tunnel.getURL();

    return tunnelUrl;
  } catch (error) {
    const errMsg = (error as Error).message;
    console.error(errMsg);
    // 重新抛出错误,让顶层调用逻辑处理
    throw new Error(errMsg);
  }
};

2. 顶层await添加错误捕获

将嵌套的立即执行async函数改为显式的try/catch,避免未处理的Promise异常导致进程退出:

let appUrl: string | undefined;
if (isProd) {
  appUrl = process.env.APP_URL;
} else {
  try {
    const { default: startTunnel } = await import("./lib/startTunnel.js");
    appUrl = await startTunnel(PORT);
  } catch (error) {
    console.error("启动隧道失败:", (error as Error).message);
    // 添加fallback逻辑,比如使用本地地址
    appUrl = `http://localhost:${PORT}`;
    // 若必须依赖隧道,可选择终止进程
    // process.exit(1);
  }
}

3. 排查外部依赖稳定性

检查untun的服务状态,或尝试切换其他隧道服务(如ngrok)作为替代,排除第三方服务故障的影响。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 20:12:42