tailwindlabs/tailwindcss · error · Error

`@utility ${node.params}` defines an invalid utility name. U

Error message

`@utility ${node.params}` defines an invalid utility name. Utilities should be alphanumeric and start with a lowercase letter.

What it means

Terminal fallback in the `createCssUtility === null` chain: the params contain no offending `*` placement but still fail the strict utility-name validation (start with a lowercase letter, alphanumeric + dashes). Typical offenders: leading uppercase, leading digit, embedded spaces, or disallowed characters.

Source

Thrown at packages/tailwindcss/src/index.ts:249

          `\`@utility ${node.params}\` is empty. Utilities should include at least one property.`,
        )
      }

      let utility = createCssUtility(node)
      if (utility === null) {
        if (!node.params.endsWith('-*')) {
          if (node.params.endsWith('*')) {
            throw new Error(
              `\`@utility ${node.params}\` defines an invalid utility name. A functional utility must end in \`-*\`.`,
            )
          } else if (node.params.includes('*')) {
            throw new Error(
              `\`@utility ${node.params}\` defines an invalid utility name. The dynamic portion marked by \`-*\` must appear once at the end.`,
            )
          }
        }

        throw new Error(
          `\`@utility ${node.params}\` defines an invalid utility name. Utilities should be alphanumeric and start with a lowercase letter.`,
        )
      }

      customUtilities.push(utility)
    }

    // Collect paths from `@source` at-rules
    if (node.name === '@source') {
      if (node.nodes.length > 0) {
        throw new Error('`@source` cannot have a body.')
      }

      if (ctx.parent !== null) {
        throw new Error('`@source` cannot be nested.')
      }

      let not = false

View on GitHub (pinned to 16e94cbf7f)

Solutions

  1. Rename to start with a lowercase letter and use only `a-z0-9-`: `@utility foo-bar`.
  2. Replace underscores with dashes: `foo_bar` → `foo-bar`.
  3. For functional utilities keep exactly one trailing `-*`: `icon-*` (not `icon-*-*`).

Example fix

/* before */
@utility Card_Primary { ... }

/* after */
@utility card-primary { ... }
Defensive patterns

Strategy: type-guard

Type guard

// Full utility-name guard covering the terminal fallback.
// Static: lowercase letter, then alphanumerics/dashes. Functional: same, plus single trailing -*.
const STATIC_UTILITY = /^[a-z][a-zA-Z0-9-]*$/
const FUNCTIONAL_UTILITY = /^[a-z][a-zA-Z0-9-]*-\*$/

function isValidUtilityName(name: string): boolean {
  return STATIC_UTILITY.test(name) || FUNCTIONAL_UTILITY.test(name)
}

Prevention

When it happens

Trigger: Naming a utility `@utility Foo`, `@utility 3col`, `@utility foo bar`, `@utility foo_bar`, or `@utility foo-*-*` (the last ends with `-*` so it bypasses the `*` checks and lands here because the double wildcard is invalid).

Common situations: Importing BEM/kebab-snake names that use underscores; capitalizing utility names by convention; copy-paste that introduces a space or stray character.

Related errors


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