tailwindlabs/tailwindcss · error · Error

Cannot use `@variant` with variant: ${variant}

Error message

Cannot use `@variant` with variant: ${variant}

What it means

Thrown by substituteAtVariant() when applyVariant() returns null, meaning the variant parsed successfully but could not be applied to the current node. This is distinct from [85]: the variant is known, but its application to the AST node failed — typically because the variant's compound/recursive logic cannot compose with the surrounding structure.

Source

Thrown at packages/tailwindcss/src/variants.ts:1310

          : variantNode.nodes.map(cloneAstNode),
      )

      let stackedVariants = segment(compoundVariant, ':')
      for (let i = stackedVariants.length - 1; i >= 0; --i) {
        let variant = stackedVariants[i].trim()

        if (!variant) {
          throw new Error(`Cannot use \`@variant\` with empty variant`)
        }

        let variantAst = designSystem.parseVariant(variant)
        if (variantAst === null) {
          throw new Error(`Cannot use \`@variant\` with unknown variant: ${variant}`)
        }

        let result = applyVariant(node, variantAst, designSystem.variants)
        if (result === null) {
          throw new Error(`Cannot use \`@variant\` with variant: ${variant}`)
        }
      }

      if (node.selector === '&') {
        nodes.push(...node.nodes)
      } else {
        nodes.push(node)
      }
    }

    // Update the variant at-rule node, to be the `&` rule node
    features |= Features.Variants
    return WalkAction.Replace(nodes)
  })
  return features
}

View on GitHub (pinned to 16e94cbf7f)

Solutions

  1. Simplify the variant stack: try the variant alone to isolate which combination fails.
  2. If it is your custom variant, ensure its static/dynamic resolver always returns a valid selector (non-null).
  3. Check the variant's definition for missing branches; report a bug if a documented built-in variant triggers this.
  4. Try a different but equivalent variant ordering.

Example fix

/* before — applyVariant returns null for this combination */
@variant my-variant:hover { ... }

/* after — verify the custom variant returns a selector */
@custom-variant my-variant (&:where(.active));
@variant my-variant { ... }
Defensive patterns

Strategy: try-catch

Validate before calling

// Limited pre-validation; applyVariant failure is structural
// Best: test the variant in isolation first
designSystem.parseVariant(name) // ensure non-null before stacking

Type guard

function variantApplies(
  node: AstNode,
  variant: Variant,
  variants: Map<string, Variant>
): boolean {
  return applyVariant(cloneAstNode(node), variant, variants) !== null
}

Try / catch

try {
  substituteAtVariant(ast, designSystem)
} catch (e) {
  if (/Cannot use `@variant` with variant:/.test(String(e))) {
    // simplify the stack and retry, or skip the problematic rule
  }
}

Prevention

When it happens

Trigger: Using a variant whose internal resolution returns null for the given node context — e.g. a custom variant with a static() strategy that doesn't apply, or stacking variants in an order that produces no valid selector. This is the fallback when parseVariant succeeds but applyVariant's machinery yields null.

Common situations: Authoring a custom @custom-variant that returns no valid selector for some inputs. Combining variants in unusual stacks. Edge cases in plugin-provided variants that don't handle all node shapes. This is rarer than [85] and often indicates a variant-definition bug rather than user error.

Related errors


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