Skip to content

Troubleshooting

Symptom first. Each entry links to the section with the full explanation.

$debug is undefined

$debug is not defined

The runtime only exists in development. Under a test runner, or in any build where the plugin is inactive, nothing installs the global.

  • In tests: the plugin registers a no-op stub automatically under Vitest. If you use Jest, a Vitest config the plugin does not reach, or a custom globalName, register it yourself: see Testing.
  • In development: register the plugin in the top-level plugins array. Under build.rollupOptions.plugins, or in a Rollup or Rolldown config, it only strips calls and installs nothing. A web worker has no dev runtime of its own: call $debug from the page, or as globalThis.$debug?.(...) in worker code. A classic inline <script> runs before the runtime loads: call $debug from module code. See Web workers.
  • In production: calls are rewritten to void 0 at build time, so the strip did not reach that call. First check that the plugin is registered for vite build: unconditionally, in the top-level plugins array, not behind command === "serve" or apply: "serve" (see Install and register the plugin). Then look for a build warning: a classic inline <script> or an on* attribute, a computed method such as $debug['time'](), a reference that is not a call (const d = $debug), a file the strip could not parse, or a script it left as written (imported with ?raw, or a TypeScript file Vite copied). See Production builds. Some calls are left without a warning: calls in a dependency (see My library's $debug calls crash the consuming app) or in a file under public/, a dynamic key such as globalThis[name](), and three rare setups (an HTML page loaded as an asset, an inline worker sharing its file name with a copied script, and a plugin that replaces worker.plugins late). See Known limits of the strip.

ESLint reports '$debug' is not defined

no-undef does not read TypeScript types. Spread the shipped flat config: see Tell ESLint about the global.

Output looks wrong

Every log appears twice

Vite does not deduplicate plugins by name. If a framework preset or a Nuxt module already adds mtDebug(), adding it yourself leaves two live instances, and the symptom is every browser log appearing twice in the terminal.

The plugin detects this: the first instance stays active, the others stand down, and you get one warning giving the count:

text
[MARS-TECH] registered 2 times — a framework preset or module has probably added it alongside your vite.config. Keeping the first instance; the rest stand down. Remove the duplicate to silence this.

Remove the duplicate to silence it. The surviving instance is the first one registered, so its options win, except redact and redactDefaults: it applies the rules of every instance that stood down as well as its own. When the duplicate comes from another installed copy of the plugin, the surviving instance cannot read its rules, which may hold string entries: $debug then prints nothing in Node and the terminal skips browser lines, with a warning, until you remove the duplicate.

Nothing in the Vue DevTools timeline

This is almost always one of two things, both due to how Vue DevTools behaves rather than to a bug:

  1. You are looking at the sidebar's Timeline tab. It only lists Mouse, Keyboard, Component events and Performance, a hard-coded set. Plugin layers are not shown there. Ours lives in the MARS-TECH tab → Timeline.
  2. Recording is off. Layers start disabled, and reopening or reloading turns them back off. Click the record button (●) at the top right of that view to arm it. Calls made before that are not recorded.

The State inspector in the same tab needs neither and is always populated: use it if you just want the latest calls. See the Vue DevTools timeline.

My log has no file:line

This is expected in two cases: logs coming from a library built with brandPrefix carry no source location by design (see Library logs carry no source location), and showSource: false turns it off globally (see the showSource option).

My log's file:line is a few lines off

The dev transform records the original position of a direct call, $debug(...) or $debug.ns("x")(...). A call it cannot record takes its position from the runtime's stack instead: a logger kept from ns() and called later (const log = $debug.ns("cart"); log(...)), count(), timeEnd(), assert() and, on the server, group() labels, table() and dir().

That position has the same format, but it is where the JavaScript engine ran the code, with no source map applied. In a compiled .vue file, a TypeScript file or an SSR module, it can be several lines off, and its column too. Call $debug(...) directly where the exact line matters.

$debug prints nothing on the server

With string entries in redact, a $debug that cannot read them prints nothing, rather than print them unmasked, and warns once:

text
[MARS-TECH] $debug prints nothing in this process: the string entries of your redact option do not reach it, and it does not print what it cannot mask. They reach SSR in the Vite process and in a worker thread started after the plugin, and Vitest workers (the forks and vmForks pools from Vitest 4). Elsewhere - SSR in a process of its own, workerd - describe each such value with a RegExp for its shape instead.

This happens, for example, with SSR in a process of its own, or in workerd (@cloudflare/vite-plugin). To see output there, replace each such string entry with a RegExp that describes the value's shape. Where string entries cannot reach lists every case, tests included, with the fix for each.

Libraries

Consumers of my library get no dev logs

The debug variant is only reachable if your package.json routes a development condition to it. The plugin warns once per build when it cannot find one: see Wire the conditional exports.

My library's $debug calls crash the consuming app

A library that ships bare $debug(...) calls without the dual variant throws a ReferenceError at runtime in the consumer's production app. The build itself succeeds, so nothing catches it earlier. Build the library with library.debugBuild: see Library authoring.