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
- Run plugin code under `deno test` (or `deno lint`), where 40_lint.js installs the real implementation
- Keep plugin entry points side-effect free so importing them elsewhere never calls runPlugin
- Guard the call with try/catch or your own context flag and skip when unavailable
- 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
- Run plugin logic only under deno test or deno lint, where the real implementation is installed
- Keep plugin modules free of top-level runPlugin calls — export registered rules instead
- Gate execution behind your own context flag or environment variable
- Watch unstable-API changelogs when upgrading Deno
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
- Expected the second argument to assertSnapshot() to be an op
- Snapshot serializer must return a string
- Missing snapshot file.
- Missing snapshot: ${name}
- Snapshot does not match:\n\n ${green("[Diff]")} ${green("
AI-assisted analysis of denoland/deno@a961cdec3b (2026-08-20).
Data as JSON: /api/errors/d49ab671aa224656.
Report an issue: GitHub.