Title: devtools-vite console pipe: infinite feedback loop with Vite 8 `server.forwardConsole` — one console entry floods browser + terminal · Issue #482 · TanStack/devtools · GitHub
Open Graph Title: devtools-vite console pipe: infinite feedback loop with Vite 8 `server.forwardConsole` — one console entry floods browser + terminal · Issue #482 · TanStack/devtools
X Title: devtools-vite console pipe: infinite feedback loop with Vite 8 `server.forwardConsole` — one console entry floods browser + terminal · Issue #482 · TanStack/devtools
Description: 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 — clie...
Open Graph Description: 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 co...
X Description: 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 s...
Opengraph URL: https://github.com/TanStack/devtools/issues/482
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"devtools-vite console pipe: infinite feedback loop with Vite 8 `server.forwardConsole` — one console entry floods browser + terminal","articleBody":"### Which project does this relate to?\n\nDevtools\n\n### Describe the bug\n\n`@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:\n\n```\n9: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…\n```\n\n**The cycle:**\n\n1. Any console entry in the browser → Vite 8 `forwardConsole` re-prints it in the terminal via the server logger (`… [vite] (client) [console.error] …`).\n2. 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.\n3. The browser mirror prints it (`%c[Server]%c …`) → Vite forwards it again → goto 1. Each hop nests the previous message inside itself.\n\nIn 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.\n\nThe 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.\n\n### Your Example Website or App\n\nSee minimal reproduction below (4 files)\n\n### Steps to Reproduce the Bug or Issue\n\n**package.json**\n```json\n{\n \"name\": \"tsd-console-loop-repro\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": { \"dev\": \"vite\" },\n \"devDependencies\": {\n \"@tanstack/devtools-vite\": \"0.8.1\",\n \"vite\": \"8.0.16\"\n }\n}\n```\n\n**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):\n```js\nimport { devtools } from '@tanstack/devtools-vite'\n\nexport default {\n appType: 'custom',\n plugins: [\n devtools(),\n {\n name: 'mini-ssr',\n configureServer(server) {\n return () => {\n server.middlewares.use(async (req, res) => {\n const { render } = await server.ssrLoadModule('/entry-server.js')\n res.setHeader('content-type', 'text/html')\n res.end(await server.transformIndexHtml(req.url ?? '/', render()))\n })\n }\n },\n },\n ],\n}\n```\n\n**entry-server.js** — the `\u003chtml` marker makes the console-pipe transform patch the server console here:\n```js\nexport function render() {\n return '\u003chtml>\u003cbody>\u003ch1>tsd console loop repro\u003c/h1>\u003cscript type=\"module\" src=\"/main.js\">\u003c/script>\u003c/body>\u003c/html>'\n}\n```\n\n**main.js** — the `createRoot` marker mimics a real client entry (which is what the client-side pipe injection keys on):\n```js\nfunction createRoot(el) {\n el.textContent = 'mounted'\n}\ncreateRoot(document.querySelector('h1'))\n\n// A single console entry seeds the infinite echo.\nconsole.error('seed')\n```\n\nThen:\n1. `pnpm install && pnpm dev`\n2. Open the page in a browser.\n3. Watch the terminal and the browser console.\n\nMeasured on this repro (Playwright counting console messages, no interaction after load):\n\n| | 10s | 20s | 30s |\n|---|---|---|---|\n| browser console entries | 189 | 374 | 559 |\n\n…growing without bound; the dev-server terminal shows the same nested `[vite] (client) [console.error] [Server]` lines.\n\n### Expected behavior\n\nOne `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.\n\n### Screenshots or Videos\n\n_No response_\n\n### Platform\n\n- OS: macOS 15 (Darwin 25.4.0)\n- Node: 22.20.0\n- vite: 8.0.16 (`server.forwardConsole` default-enabled)\n- @tanstack/devtools-vite: 0.8.1 (also reproduces on 0.7.0)\n\n### Additional context\n\nWe'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:\n\n```diff\n // Skip our own TSD Console Pipe logs to avoid recursion/noise\n if (args.length > 0 && typeof args[0] === 'string' &&\n (args[0].indexOf('[TSD Console Pipe]') !== -1 ||\n args[0].indexOf('[@tanstack/devtools') !== -1)) {\n return;\n }\n+\n+ // Vite 8's server.forwardConsole re-prints CLIENT console output in\n+ // the terminal (\"[vite] (client) [console.warn] ...\"). Those are not\n+ // server logs — re-broadcasting them to the browser makes the browser\n+ // log them again, which vite forwards again: an infinite\n+ // client->server->client echo. Skip vite-forwarded client logs.\n+ if (isServer && args.length > 0 && typeof args[0] === 'string' &&\n+ (args[0].indexOf('(client)') !== -1 ||\n+ args[0].indexOf('[console.') !== -1)) {\n+ return;\n+ }\n```\n\nWith 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.\n","author":{"url":"https://github.com/rshelnutt","@type":"Person","name":"rshelnutt"},"datePublished":"2026-07-08T01:29:21Z","url":"https://github.com/TanStack/devtools/issues/482"}
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:e02935e6-69c0-5f92-1444-417719ec7c29 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | C14A:3E915:180C58:21D7C4:6AF9A149 |
| html-safe-nonce | 0f8edfe3515041ae53761c6ea59f1bc01816190e3f56eb602e9336d51ff3c7b6 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDMTRBOjNFOTE1OjE4MEM1ODoyMUQ3QzQ6NkFGOUExNDkiLCJ2aXNpdG9yX2lkIjoiNTkyMzE3Njg3NDU1NjU0MjE3IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 75e629ec07c4304715c5ada625403baab17200a784f83092d9c768b1e4e3ab43 |
| hovercard-subject-tag | issue:4833526249 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/TanStack/devtools/482/issue_layout |
| twitter:image | https://opengraph.githubassets.com/0298c8d299d10c44d967b88fdeb74158b63840b2d3bc1e14152ce979d9f1e1cd/TanStack/devtools/issues/482 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/0298c8d299d10c44d967b88fdeb74158b63840b2d3bc1e14152ce979d9f1e1cd/TanStack/devtools/issues/482 |
| og:image:alt | 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 co... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | rshelnutt |
| hostname | github.com |
| expected-hostname | github.com |
| None | 612d3e6ffd693e7ea8d9fcf452370995872baef31d825b1fedef704cd3d24ac7 |
| turbo-cache-control | no-preview |
| go-import | github.com/TanStack/devtools git https://github.com/TanStack/devtools.git |
| octolytics-dimension-user_id | 72518640 |
| octolytics-dimension-user_login | TanStack |
| octolytics-dimension-repository_id | 1026402761 |
| octolytics-dimension-repository_nwo | TanStack/devtools |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 1026402761 |
| octolytics-dimension-repository_network_root_nwo | TanStack/devtools |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 072e34035c13c31543fba9f025cbbbda4210dae4 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
| react-app-name | issues |
Links:
Viewport: width=device-width