denoland/deno · error · Error

`Deno.lint.runPlugin` is only available in `deno test` subco

Error message

`Deno.lint.runPlugin` is only available in `deno test` subcommand.

What it means

In the standard runtime bootstrap, Deno.lint is a compatibility stub: test and bench are no-ops, and lint.runPlugin is a function whose only job is to throw, stating it is available only under the lint-capable subcommands. The real implementation is installed later by cli/js/40_lint.js when the process runs `deno lint` (and lint plugin tests under `deno test`); anywhere else the call throws this Error.

Source

Thrown at runtime/js/99_main.js:814

noopTest.only.each = noopTestEach;
noopTest.ignore.each = noopTestEach;
// Build finalDenoNs without spreading denoNs: spread invokes every getter,
// including the lazy ones (Deno.serve / Deno.run / etc.) that intentionally
// avoid loading 06_streams / 22_body / 40_process at snapshot time. Use
// ObjectDefineProperties + getOwnPropertyDescriptors to preserve the lazy
// descriptors.
const finalDenoNs = ObjectDefineProperties(
  {
    internal: internalSymbol,
    [internalSymbol]: internals,
    // Deno.test, Deno.bench, Deno.lint are noops here, but kept for
    // compatibility; so that they don't cause errors when used outside of
    // `deno test`/`deno bench`/`deno lint` contexts.
    test: noopTest,
    bench: () => {},
    lint: {
      runPlugin: () => {
        throw new Error(
          "`Deno.lint.runPlugin` is only available in `deno test` subcommand.",
        );
      },
    },
  },
  getSafeOwnPropertyDescriptors(denoNs),
);

ObjectDefineProperties(finalDenoNs, {
  pid: core.propGetterOnly(opPid),
  // `ppid` should not be memoized.
  // https://github.com/denoland/deno/issues/23004
  ppid: core.propGetterOnly(() => op_ppid()),
  noColor: core.propGetterOnly(() => op_bootstrap_no_color()),
  args: core.propGetterOnly(opArgs),
  mainModule: core.propGetterOnly(() => op_main_module()),
  exitCode: {
    __proto__: null,

View on GitHub (pinned to a961cdec3b)

Solutions

  1. Run plugin code under `deno test` (or `deno lint`), where 40_lint.js installs the real implementation
  2. Keep plugin entry points side-effect free so importing them elsewhere never calls runPlugin
  3. Guard the call with try/catch or your own context flag and skip when unavailable
  4. Check the current Deno version's lint plugin docs — the API is unstable and has shifted between releases

Example fix

// before
Deno.lint.runPlugin(visitor, ast);

// after
try {
  Deno.lint.runPlugin(visitor, ast);
} catch (e) {
  if (e instanceof Error && e.message.includes('only available')) return; // not a lint context
  throw e;
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  Deno.lint.runPlugin(visitor, ast);
} catch (e) {
  if (e instanceof Error && e.message.includes('only available')) {
    return; // not running under deno test / deno lint — skip
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling Deno.lint.runPlugin(...) under `deno run`, `deno repl`, or in module code imported into a normal program; sharing lint-plugin source files between lint rules and regular scripts so the call executes in the wrong context.

Common situations: Lint plugin development where plugin modules are exercised by `deno test`; CI jobs importing plugin files for type-checking that accidentally execute them; unstable API churn across Deno versions.

Related errors


AI-assisted analysis of denoland/deno@a961cdec3b (2026-08-20). Data as JSON: /api/errors/d49ab671aa224656. Report an issue: GitHub.