denoland/deno · critical · Error
Linter plugin must be an object
Error message
${prefix}Linter plugin must be an object What it means
A path-traversal (Zip Slip) guard inside the jsr-compat tarball extraction: each entry's path is stripped of its leading package-root component, and the stripped remainder is rejected if it is absolute or contains `..`/root components — i.e. if the entry would resolve outside the destination directory. This is a deliberate security refusal, not a bug to work around; a hit strongly suggests a maliciously or corruptly packed tarball.
Solutions
- Do NOT bypass the guard — treat it as a supply-chain red flag
- Identify which dependency it came from (the log names the package being installed just above) and inspect it: check its JSR page, source repo, and recent version bumps
- Pin the dependency to the last known-good version in deno.json / lockfile until the package is fixed
- Report the package to JSR abuse (security@jsr.io) and rotate any secrets if the code was ever executed
Example fix
// deno.json — before (pulling compromised latest)
"imports": { "pkg": "jsr:@evil/pkg" }
// after (pin known-good, audit)
"imports": { "pkg": "jsr:@evil/pkg@0.9.1" } // last clean version, pending maintainer fix Defensive patterns
Strategy: validation
Validate before calling
# Audit dependency tarballs for traversal entries before wide rollout # (the guard itself is the runtime validation — this is defense in depth) deno info --json 2>/dev/null | jq '.modules[].specifier' # review your remote deps
Try / catch
Never catch-and-continue this error. In automation, match /Refusing to extract tar entry outside dest/ and escalate immediately: halt the build, notify security, pin the offending dependency to its last known-good version, and audit lockfile diffs.
Prevention
- Treat this error as a supply-chain alert, never as noise to suppress
- Commit and review lockfile diffs in CI so new package versions get human eyes
- Subscribe to JSR/npm advisories for your dependencies
- If triggered, rotate credentials that the compromised package could have touched
When it happens
Trigger: Extracting a jsr compat tarball (downloaded from npm.jsr.io during `deno check`/install with stock tsc) that contains entries like `pkg/../../evil.sh`, absolute paths (`/etc/...`), or root-component paths. The check operates on the path after skipping the first component, so only genuinely escaping entries are refused.
Common situations: Supply-chain attack via a compromised package; a corrupted download producing garbage paths; a broken pack tool emitting absolute entry names. Genuine packages virtually never trip this guard.
Related errors
- refusing tar entry with traversal path
- refusing zip entry with traversal path
- refusing zip entry with unsafe path
- refusing symlink entry in laufey archive
- refusing tar entry that would unpack outside dest
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/ff32b942149b9499.
Report an issue: GitHub.
Appendix: source
Thrown at cli/js/40_lint.js:483
}
}
return plugins.map((plugin, i) => installPlugin(plugin, specifiers?.[i]));
}
/**
* @param {Deno.lint.Plugin} plugin
* @param {string} [specifier] The specifier the plugin was loaded from, if any.
*/
function installPlugin(plugin, specifier) {
// When the plugin was loaded from a specifier (i.e. `lint.plugins` in the
// config), prefix validation errors with it so the user knows which plugin
// is misbehaving instead of getting an anonymous error.
const prefix = typeof specifier === "string"
? `Failed to load lint plugin '${specifier}': `
: "";
if (typeof plugin !== "object") {
throw new Error(`${prefix}Linter plugin must be an object`);
}
if (typeof plugin.name !== "string") {
throw new Error(`${prefix}Linter plugin name must be a string`);
}
if (!/^[a-z-]+$/.test(plugin.name)) {
throw new Error(
`${prefix}Linter plugin name must only contain lowercase letters (a-z) or hyphens (-).`,
);
}
if (plugin.name.startsWith("-") || plugin.name.endsWith("-")) {
throw new Error(
`${prefix}Linter plugin name must start and end with a lowercase letter.`,
);
}
if (plugin.name.includes("--")) {
throw new Error(
`${prefix}Linter plugin name must not have consequtive hyphens.`,
);View on GitHub (pinned to 9ad36f7a2c)