ESLint flat config failures: rule not found, missing plugins, and silent skips
A diagnostic guide for ESLint v9 flat config failures: rule not found, missing plugins, and files silently skipped after migrating from .eslintrc to eslint.config.js.
28 May 2026, 11:27 UTC

After migrating from .eslintrc to eslint.config.js you get "Definition for rule X was not found", "Cannot find module eslint-plugin-...", or files that used to be linted are now silently skipped. The useful takeaway is that ESLint v9 flat config does not inherit .eslintrc behavior: plugins must be imported and registered explicitly, ignores are not read from .eslintignore, and files are matched only by the config's files patterns. Fix the wiring, not the rules.
Recognizable conditions in ESLint v9+
- Rule not found errors for rules you previously extended.
- Module resolution errors for eslint-plugin-* packages.
- Files unexpectedly linted with no rules or reported as ignored.
- TypeScript parser errors or false positives on no-unused-vars when using @typescript-eslint.
Cause / symptom table
| Symptom | Most likely cause in flat config |
|---|---|
| Definition for rule X was not found | Plugin not imported and registered in plugins object. String-based plugin names from .eslintrc are no longer valid. |
| Cannot find module eslint-plugin-... | Plugin not installed as a direct devDependency or resolved relative to the config file location, not ESLint's location. |
| Files silently skipped or linted with defaults | No files pattern matches the file, or ignores are missing. Flat config does not read .eslintignore. |
| Nested package rules missing in monorepo | Nested .eslintrc files are not auto-discovered. Each package needs its rules merged into a root eslint.config.js or its own flat config. |
| Parser errors / type-aware false positives | @typescript-eslint parser/plugin missing or projectService/project option misconfigured for typed linting. |
- Identify the config file ESLint actually loads. Run in the project root where package.json lives:
Required permission is read access to the repo. Risk is none; debug output is verbose.npx eslint --debug 2>&1 | grep config file - Inspect the resolved config for a representative file:
Replace path/to/file.js with a real file. Check that rules, plugins, and parser appear in the printed object.npx eslint --print-config path/to/file.js - Verify installed versions:
Compare each plugin with its README for flat-config support. Some community plugins lagged flat config exports.npm ls eslint - Confirm files and ignores coverage. List the files patterns in eslint.config.js and ensure they are relative to the config file location.
Rule not found -> import and register the plugin
In flat config plugins are imported and referenced by key. Example shape:
import js from "@eslint/js";
import tseslint from "typescript-eslint";
export default [
{
files: ["**/*.ts"],
plugins: { js },
languageOptions: { parser: tseslint.parser },
rules: {"no-unused-vars": "error" }
}
];Do not use string names like "eslint-plugin-react". Import the module and place it in plugins.
Module not found -> install and resolve correctly
Flat config resolves imports relative to eslint.config.js. Ensure the plugin is a direct devDependency in the same package where the config lives. Do not rely on hoisted node_modules in a monorepo without proper resolution.
Skipped or over-linted files -> declare files and ignores
Flat config ignores .eslintignore. Declare ignores in a config object that contains only the ignores key, or in each config object:
export default [
{ ignores: ["dist/**", "node_modules/**"] },
{ files: ["src/**/*.js"], rules: { ... } }
];Files outside files patterns receive no config.
Legacy shareable configs -> wrap with FlatCompat
For shareable configs written for .eslintrc, use @eslint/eslintrc FlatCompat to adapt them:
import { FlatCompat } from "@eslint/eslintrc";
const compat = new FlatCompat({ baseDirectory: import.meta.dirname });
export default [...compat.extends("plugin:react/recommended")];Check the plugin's documentation for a native flat export before using compat.
Monorepo and TypeScript
Merge package rules into the root config or run ESLint from the package directory with its own eslint.config.js. For typed linting, ensure @typescript-eslint is installed and projectService is configured with a valid tsconfig path. Misconfiguration yields parser errors or incorrect no-unused-vars results.
Verification
After changes, re-run --print-config for the same file and confirm the expected rules and plugins appear. Deliberately introduce a known violation in a target file and confirm the expected rule reports. This proves wiring without assuming output.
Escalation criteria
Escalate when the failure persists after config inspection with --print-config, when a required plugin has no flat-config-compatible release, or when typed linting causes severe performance degradation on large repos. Tune projectService, limit files patterns, or disable type-aware rules as a mitigation.
Limitations
Behavior differs between ESLint v8 opt-in and v9 default. Environment variable ESLINT_USE_FLAT_CONFIG is version-sensitive and removed in newer majors. Some plugins historically lack flat config support. Keep a backup of the previous .eslintrc before migration to allow rollback if CI breaks.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.