TanStack / TanStack/devtools

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

Aperta
#482 3 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Lingua principale
TypeScript
Stelle
499
Fork
100
Merge medio
1g 17h
PR unite (30g)
4

Descrizione

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.

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia con la riproduzione nei quattro file: vite.config.js, entry-server.js, main.js e package.json, quindi segui l’iniezione di console-pipe lato server usata dall’entry SSR. Riproduci il problema con Vite 8 e osserva sia l’output del browser sia quello del terminale. Il lavoro è completato quando un errore di console preimpostato rimane circoscritto, i log client inoltrati da Vite non vengono ritrasmessi e i log server autentici continuano a essere rispecchiati una sola volta.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
typescript, vite
Ambito
build-system, devtools
Tipo di issue
Bug
Difficoltà
3/5
Tempo stimato
1-2 giorni
Stato di attività
Tranquilla
Chiarezza
Specificata chiaramente
Idoneità per principianti
72/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.