tailwindlabs/tailwindcss · error · Error

`@plugin` cannot be nested.

Error message

`@plugin` cannot be nested.

What it means

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.

Source

Thrown at packages/tailwindcss/src/compat/apply-compat-hooks.ts:61

  let pluginPaths: [
    { id: string; base: string; reference: boolean; src: SourceLocation | undefined },
    CssPluginOptions | null,
  ][] = []
  let configPaths: {
    id: string
    base: string
    reference: boolean
    src: SourceLocation | undefined
  }[] = []

  walk(ast, (node, _ctx) => {
    if (node.kind !== 'at-rule') return
    let ctx = cssContext(_ctx)

    // Collect paths from `@plugin` at-rules
    if (node.name === '@plugin') {
      if (ctx.parent !== null) {
        throw new Error('`@plugin` cannot be nested.')
      }

      let pluginPath = node.params.slice(1, -1)
      if (pluginPath.length === 0) {
        throw new Error('`@plugin` must have a path.')
      }

      let options: CssPluginOptions = {}

      for (let decl of node.nodes ?? []) {
        if (decl.kind !== 'declaration') {
          throw new Error(
            `Unexpected \`@plugin\` option:\n\n${toCss([decl])}\n\n\`@plugin\` options must be a flat list of declarations.`,
          )
        }

        if (decl.value === undefined) continue

View on GitHub (pinned to 16e94cbf7f)

Solutions

  1. Move the `@plugin` at-rule to the top level of the stylesheet, outside any block.
  2. Remove any surrounding `@layer`/`@media`/selector wrapper around the `@plugin`.

Example fix

// before
@layer base {
  @plugin "./my-plugin.js";
}
// after
@plugin "./my-plugin.js";
Defensive patterns

Strategy: validation

Validate before calling

function assertPluginAtTopLevel(ast) {
  walk(ast, (node, ctx) => {
    if (node.kind === 'at-rule' && node.name === '@plugin' && ctx.parent !== null) {
      throw new Error('@plugin must be top-level');
    }
  });
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of tailwindlabs/tailwindcss@16e94cbf7f (2026-08-12). Data as JSON: /api/errors/9d91840d6ce3d87d. Report an issue: GitHub.