{"record":{"id":"9d91840d6ce3d87d","repo":"tailwindlabs/tailwindcss","slug":"plugin-cannot-be-nested","errorCode":null,"errorMessage":"`@plugin` cannot be nested.","messagePattern":"`@plugin` cannot be nested\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/tailwindcss/src/compat/apply-compat-hooks.ts","lineNumber":61,"sourceCode":"  let pluginPaths: [\n    { id: string; base: string; reference: boolean; src: SourceLocation | undefined },\n    CssPluginOptions | null,\n  ][] = []\n  let configPaths: {\n    id: string\n    base: string\n    reference: boolean\n    src: SourceLocation | undefined\n  }[] = []\n\n  walk(ast, (node, _ctx) => {\n    if (node.kind !== 'at-rule') return\n    let ctx = cssContext(_ctx)\n\n    // Collect paths from `@plugin` at-rules\n    if (node.name === '@plugin') {\n      if (ctx.parent !== null) {\n        throw new Error('`@plugin` cannot be nested.')\n      }\n\n      let pluginPath = node.params.slice(1, -1)\n      if (pluginPath.length === 0) {\n        throw new Error('`@plugin` must have a path.')\n      }\n\n      let options: CssPluginOptions = {}\n\n      for (let decl of node.nodes ?? []) {\n        if (decl.kind !== 'declaration') {\n          throw new Error(\n            `Unexpected \\`@plugin\\` option:\\n\\n${toCss([decl])}\\n\\n\\`@plugin\\` options must be a flat list of declarations.`,\n          )\n        }\n\n        if (decl.value === undefined) continue\n","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/tailwindlabs/tailwindcss/blob/16e94cbf7f965c5ad697e90e940b5e178efad67c/packages/tailwindcss/src/compat/apply-compat-hooks.ts#L43-L79","documentation":"Thrown by the v3-compat hook when an `@plugin` at-rule is encountered with a non-null `ctx.parent`, i.e. it is nested inside another rule or at-rule. `@plugin` must be top-level so Tailwind can resolve and load the module at the right phase.","triggerScenarios":"Writing `@plugin \"./my-plugin.js\";` inside a `@layer`, `@media`, or any selector rule. The walker sets `ctx.parent` for nested nodes, and the guard rejects it.","commonSituations":"Indenting `@plugin` under a rule by accident; converting a JS config to CSS `@plugin` and leaving it scoped; editor auto-indent pushing it inside a block.","solutions":["Move the `@plugin` at-rule to the top level of the stylesheet, outside any block.","Remove any surrounding `@layer`/`@media`/selector wrapper around the `@plugin`."],"exampleFix":"// before\n@layer base {\n  @plugin \"./my-plugin.js\";\n}\n// after\n@plugin \"./my-plugin.js\";","handlingStrategy":"validation","validationCode":"function assertPluginAtTopLevel(ast) {\n  walk(ast, (node, ctx) => {\n    if (node.kind === 'at-rule' && node.name === '@plugin' && ctx.parent !== null) {\n      throw new Error('@plugin must be top-level');\n    }\n  });\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Place all `@plugin` and `@config` at-rules at the very top of your entry CSS file.","Review diff indentation when merging CSS to catch accidental nesting."],"tags":["tailwind","plugin","at-rule","nesting"],"backgroundTag":null,"analyzedSha":"16e94cbf7f965c5ad697e90e940b5e178efad67c","analyzedAt":"2026-08-12T06:02:42.469Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}