denoland/deno · error · Error
Linter plugin name must start and end with a lowercase…
Error message
${prefix}Linter plugin name must start and end with a lowercase letter. What it means
After the charset check, Deno's lint plugin loader also rejects plugin names whose first or last character is a hyphen (plugin.name.startsWith("-") || plugin.name.endsWith("-") in cli/js/40_lint.js). The rule exists so names are always parseable as kebab-case tokens inside rule ids like "plugin-name/rule-name". The message is prefixed with "Failed to load lint plugin '<specifier>': " when the plugin came from a specifier.
Solutions
- Remove the leading or trailing hyphen so the name starts and ends with a lowercase letter ("my-plugin").
- If the name is generated, trim '-' from both ends: name.replace(/^-+|-+$/g, "").
- Re-run `deno lint` to confirm the plugin installs.
Example fix
// before
export default {
name: "-strict-rules-",
rules: {},
};
// after
export default {
name: "strict-rules",
rules: {},
}; Defensive patterns
Strategy: validation
Validate before calling
function normalizePluginName(name) {
return String(name).replace(/[^a-z-]+/g, "-").replace(/^-+|-+$/g, "");
}
const plugin = { name: normalizePluginName(rawName), rules }; Type guard
/** @param {unknown} name @returns {boolean} */
function hasCleanNameEdges(name) {
return typeof name === "string" && /^[a-z](.*[a-z])?$/.test(name);
} Try / catch
try {
installPlugin(plugin);
} catch (err) {
if (err.message.includes("must start and end with a lowercase letter")) {
plugin.name = plugin.name.replace(/^-+|-+$/g, ""); // then retry once
} else throw err;
} Prevention
- When assembling names from parts, filter empty segments before joining with '-'.
- Include the edge check (no leading/trailing '-') in the plugin's own unit test.
When it happens
Trigger: Installing a lint plugin whose `name` is "-my-plugin", "my-plugin-", or just "-". The name already passed /^[a-z-]+$/ (so it is only lowercase letters and hyphens) but has a leading or trailing hyphen.
Common situations: Hand-truncating a longer name and leaving a hyphen ("my-plugin" cut to "-plugin"); generating names by splitting a phrase on spaces and joining with '-' including empty parts; typos.
Related errors
- Linter plugin name must not have consequtive hyphens.
- Linter plugin name must only contain lowercase letters…
- Linter plugin rules must be an object
- Destroy hook of " " errored
- Empty node
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/b1a56e22209271a7.
Report an issue: GitHub.
Appendix: source
Thrown at cli/js/40_lint.js:494
// 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.`,
);
}
if (typeof plugin.rules !== "object") {
throw new Error(`${prefix}Linter plugin rules must be an object`);
}
if (state.installedPlugins.has(plugin.name)) {
throw new Error(`Linter plugin ${plugin.name} has already been registered`);
}
state.plugins.push(plugin);
state.installedPlugins.add(plugin.name);
return {View on GitHub (pinned to 9ad36f7a2c)