Skip to content

openspec archive 陷入无限循环,ora spinner 在非 TTY 环境将 ANSI 逃逸序列重复写入 stdout 导致磁盘写满(19.8GB) #1526

Description

@Dogwinmazhi

环境信息

项目 详情
OpenSpec 版本 1.6.0(本地)/ 1.8.0(npm latest)
Node.js v20+
操作系统 Windows 11 Home China 10.0.22631
运行方式 Claude Code 后台任务(Task tool)
终端 非交互式(管道/文件重定向)

复现步骤

  1. 在一个有 OpenSpec changes 的项目中,通过 Claude Code 的 Task 工具(后台任务模式)执行:
    openspec archive cloud-config-sync
    
  2. 该命令输出交互式提示:Warning: 15 incomplete task(s) found. Continue? (y/N)
  3. 自动回答 y 后,命令进入 spinner 渲染阶段
  4. spinner 陷入死循环,持续向 stdout 写入 ANSI 光标移动序列

实际行为

命令输出文件在约 2 天内增长到 19.8 GB,内容为以下模式的无限重复:

[54G[55G[54G[55G[54G[55G[54G[55G...

[54G[55G 是 ANSI CSI 光标水平定位序列(移动到第 54 列、第 55 列),spinner 在相邻两列之间来回跳动,形成死循环。

整个文件只有 4 行,其中第 2 行占了 19.8GB 的全部内容。

期望行为

  1. 在非交互式终端(stdout 非 TTY)中,spinner 应降级为静态文本或完全禁用,不应输出 ANSI 逃逸序列
  2. 即使 spinner 被启用,也不应出现无限循环——应有超时或最大帧数限制
  3. 命令执行完成后应正常退出

根因分析(初步)

问题很可能出在 ora(v9.4.1)spinner 库与 @inquirer/prompts 的交互上:

  1. 非 TTY 环境未检测:当 stdout 被管道重定向到文件时,ora 应检测到 process.stdout.isTTY === false 并禁用动画。但此处 spinner 仍然在输出 ANSI 序列,说明 TTY 检测可能被绕过或未生效。

  2. spinner 渲染循环未退出ora 内部使用 setInterval 循环渲染 spinner 帧。如果停止条件(spinner.stop())因异常未触发,渲染循环会永远运行。

  3. 交互式提示在后台任务中的适配问题@inquirer/promptsconfirm 提示在非 TTY 环境下收到管道输入 y\n 后,可能触发了某种异常流程,导致 spinner 启动了但永远不会被停止。

严重程度

— 在后台任务/CI 环境中可能导致磁盘空间耗尽,影响系统稳定性。

日志片段

文件开头(正常部分):

Proposal warnings in proposal.md (non-blocking):
  ⚠ Consider splitting changes with more than 10 deltas
Task status: 0/15 tasks
? Warning: 15 incomplete task(s) found. Continue? (y/N) y
✔ Warning: 15 incomplete task(s) found. Continue? Yes

之后即为无限的 [54G[55G[54G[55G... 序列。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions