TanStack / TanStack/devtools

devtools-vite console pipe: infinite feedback loop with Vite 8 `server.forwardConsole` — one console entry floods browser + terminal

未关闭
#482 3 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

主要语言
TypeScript
星标
499
派生
100
平均合并
1 天 17 小时
30 天内合并 PR
4

描述

Which project does this relate to?

Devtools

Describe the bug

@tanstack/devtools-vite's console pipe enters an infinite feedback loop with Vite 8's server.forwardConsole (on by default): a single console.error/console.warn anywhere — client or server — floods both the browser console and the terminal with thousands of nested lines per minute, indefinitely:

9:27:59 PM [vite] (client) [console.error] [Server] 9:27:59 PM [vite] (client) [console.error] [Server] 9:27:59 PM [vite] (client) [console.error] [Se…

The cycle:

  1. Any console entry in the browser → Vite 8 forwardConsole re-prints it in the terminal via the server logger (… [vite] (client) [console.error] …).
  2. The console-pipe's server-side console patch intercepts that print. Its recursion guard only skips messages containing [TSD Console Pipe] / [@tanstack/devtools, so a Vite-forwarded client log is treated as a server log and broadcast to the browser over SSE.
  3. The browser mirror prints it (%c[Server]%c …) → Vite forwards it again → goto 1. Each hop nests the previous message inside itself.

In a real TanStack Start app (Vite 8.0.16, devtools-vite 0.8.1) we measured one failed fetch turning into 37,779 browser console entries in 30s and a 2.5 GB terminal log in about a minute.

The related fix in 0.6.1 ("console pipe feedback loop … with Vite 8") does not cover this path — 0.7.0 and 0.8.1 both reproduce.

Your Example Website or App

See minimal reproduction below (4 files)

Steps to Reproduce the Bug or Issue

package.json

{
  "name": "tsd-console-loop-repro",
  "private": true,
  "type": "module",
  "scripts": { "dev": "vite" },
  "devDependencies": {
    "@tanstack/devtools-vite": "0.8.1",
    "vite": "8.0.16"
  }
}

vite.config.js — bare Vite + devtools + a 10-line SSR middleware (the pipe's server console patch is only injected into SSR modules, so a plain SPA doesn't show it):

import { devtools } from '@tanstack/devtools-vite'

export default {
  appType: 'custom',
  plugins: [
    devtools(),
    {
      name: 'mini-ssr',
      configureServer(server) {
        return () => {
          server.middlewares.use(async (req, res) => {
            const { render } = await server.ssrLoadModule('/entry-server.js')
            res.setHeader('content-type', 'text/html')
            res.end(await server.transformIndexHtml(req.url ?? '/', render()))
          })
        }
      },
    },
  ],
}

entry-server.js — the <html marker makes the console-pipe transform patch the server console here:

export function render() {
  return '<html><body><h1>tsd console loop repro</h1><script type="module" src="/main.js"></script></body></html>'
}

main.js — the createRoot marker mimics a real client entry (which is what the client-side pipe injection keys on):

function createRoot(el) {
  el.textContent = 'mounted'
}
createRoot(document.querySelector('h1'))

// A single console entry seeds the infinite echo.
console.error('seed')

Then:

  1. pnpm install && pnpm dev
  2. Open the page in a browser.
  3. Watch the terminal and the browser console.

Measured on this repro (Playwright counting console messages, no interaction after load):

10s 20s 30s
browser console entries 189 374 559

…growing without bound; the dev-server terminal shows the same nested [vite] (client) [console.error] [Server] lines.

Expected behavior

One console.error('seed') produces one line in the browser and (via Vite's forwarding) one line in the terminal. Vite-forwarded client logs should not be re-broadcast to the browser as server logs.

Screenshots or Videos

No response

Platform
  • OS: macOS 15 (Darwin 25.4.0)
  • Node: 22.20.0
  • vite: 8.0.16 (server.forwardConsole default-enabled)
  • @tanstack/devtools-vite: 0.8.1 (also reproduces on 0.7.0)
Additional context

We're currently carrying this as a pnpm patch — extending the recursion guard in the generated console-pipe code so the server-side patch skips Vite-forwarded client logs:

         // Skip our own TSD Console Pipe logs to avoid recursion/noise
         if (args.length > 0 && typeof args[0] === 'string' &&
             (args[0].indexOf('[TSD Console Pipe]') !== -1 ||
              args[0].indexOf('[@tanstack/devtools') !== -1)) {
           return;
         }
+
+        // Vite 8's server.forwardConsole re-prints CLIENT console output in
+        // the terminal ("[vite] (client) [console.warn] ..."). Those are not
+        // server logs — re-broadcasting them to the browser makes the browser
+        // log them again, which vite forwards again: an infinite
+        // client->server->client echo. Skip vite-forwarded client logs.
+        if (isServer && args.length > 0 && typeof args[0] === 'string' &&
+            (args[0].indexOf('(client)') !== -1 ||
+             args[0].indexOf('[console.') !== -1)) {
+          return;
+        }

With that guard in place: same app, same seed error — browser console stays at 5 entries (flat), terminal stays quiet, and genuine server logs still mirror into the browser once, as intended. A marker-based handshake between the pipe and Vite's forwarder would probably be more robust than string matching, but the diff above demonstrates the loop and the fix.

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

先从四个文件的复现开始:vite.config.js、entry-server.js、main.js 和 package.json,然后跟踪 SSR 入口使用的服务端 console-pipe 注入。使用 Vite 8 进行复现,并观察浏览器和终端的输出。完成的标准是:一个预置的 console 错误保持在有限范围内,Vite 转发的客户端日志不会被再次广播,而真正的服务端日志仍然只镜像一次。

由索引模型根据 Issue 内容生成。

评估

技术栈
typescript, vite
领域
build-system, devtools
Issue 类型
缺陷
难度
3/5
预计耗时
1-2 天
活跃度
冷清
描述清晰度
描述清楚
新手友好度
72/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。