tailwindlabs/tailwindcss · error · Error

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

Error message

`@utility ${node.params}` defines an invalid utility name. A functional utility must end in `-*`.

What it means

Thrown from the fallback chain when `createCssUtility(node)` returns null and the params end with `*` but do not end with `-*`. Functional (dynamic) utilities must use the `-*` suffix to mark the dynamic slot, e.g. `@utility icon-*`. A trailing `*` without the dash (like `icon*` or `icon_bar*`) is rejected because it cannot be unambiguously split into a static prefix and a dynamic value.

Source

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

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

      if (node.nodes.length === 0) {
        throw new Error(
          `\`@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) {

View on GitHub (pinned to 16e94cbf7f)

Solutions

  1. Use the `-*` suffix: rename `@utility icon*` to `@utility icon-*`.
  2. If you intended a static utility, remove the trailing `*` entirely (`@utility icon`).
  3. Verify the resulting name matches `/^[a-z][a-zA-Z0-9-]*-\*$/` for functional utilities.

Example fix

/* before */
@utility icon* {
  background-image: url('/icons/icon.svg');
}

/* after */
@utility icon-* {
  background-image: url('/icons/icon--value.svg');
}
Defensive patterns

Strategy: validation

Validate before calling

// Mirror the library's functional-utility name checks.
const ENDS_STAR_NOT_DASH_STAR = /^[a-z][a-zA-Z0-9-]*\*$/ // ends with *, not -*

function validateUtilityName(name: string): string | null {
  if (ENDS_STAR_NOT_DASH_STAR.test(name) && !name.endsWith('-*')) {
    return `Functional utility '${name}' must end in -*`
  }
  return null
}

Prevention

When it happens

Trigger: Defining `@utility icon* { ... }` or any name ending in `*` where the character before the `*` is not `-`.

Common situations: Assuming any trailing `*` works as a wildcard; translating a naming scheme from another system (e.g. `icon*-${name}`) into Tailwind v4 syntax.

Related errors


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