tailwindlabs/headlessui · error · Error

Missing Focus.First, Focus.Previous, Focus.Next or Focus.Las

Error message

Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last

What it means

Thrown by Headless UI's internal focusIn() helper when the focus flag passed to it does not include any directional bit (Focus.First, Focus.Previous, Focus.Next, or Focus.Last). focusIn() computes a movement direction from these flags; without one, there is no direction to move and the API contract is violated. This almost always means a caller passed 0, an undefined value, or only modifier flags like Focus.NoScroll.

Source

Thrown at packages/@headlessui-react/src/utils/focus-management.ts:280

  if (skipElements.length > 0 && elements.length > 1) {
    elements = elements.filter(
      (element) =>
        !skipElements.some(
          (skipElement) =>
            skipElement != null && 'current' in skipElement
              ? skipElement?.current === element // Handle MutableRefObject
              : skipElement === element // Handle HTMLElement directly
        )
    )
  }

  relativeTo = relativeTo ?? (root?.activeElement as HTMLElement)

  let direction = (() => {
    if (focus & (Focus.First | Focus.Next)) return Direction.Next
    if (focus & (Focus.Previous | Focus.Last)) return Direction.Previous

    throw new Error('Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last')
  })()

  let startIndex = (() => {
    if (focus & Focus.First) return 0
    if (focus & Focus.Previous) return Math.max(0, elements.indexOf(relativeTo)) - 1
    if (focus & Focus.Next) return Math.max(0, elements.indexOf(relativeTo)) + 1
    if (focus & Focus.Last) return elements.length - 1

    throw new Error('Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last')
  })()

  let focusOptions = focus & Focus.NoScroll ? { preventScroll: true } : {}

  let offset = 0
  let total = elements.length
  let next = undefined
  do {
    // Guard against infinite loops

View on GitHub (pinned to eea57cf46f)

Solutions

  1. Pass a directional flag: use focusIn(elements, Focus.First) or combine with modifiers, e.g. focusIn(elements, Focus.First | Focus.NoScroll).
  2. If the flag comes from a variable, verify it is always one of Focus.First/Previous/Next/Last (optionally OR-ed with Focus.NoScroll) and never 0/undefined.
  3. Check for duplicate @headlessui-react installs (npm ls @headlessui/react) and dedupe so enum values are consistent.

Example fix

// before
focusIn(elements, Focus.NoScroll) // no direction bit -> throws

// after
focusIn(elements, Focus.First | Focus.NoScroll)
Defensive patterns

Strategy: validation

Validate before calling

import { Focus } from '@headlessui/react'

const DIRECTION = Focus.First | Focus.Previous | Focus.Next | Focus.Last
function isValidFocusFlag(focus: number): boolean {
  return (focus & DIRECTION) !== 0
}

// before calling:
if (!isValidFocusFlag(flags)) flags = Focus.First
focusIn(elements, flags)

Type guard

const hasDirection = (f: number): f is number => (f & (Focus.First | Focus.Previous | Focus.Next | Focus.Last)) !== 0

Try / catch

try { focusIn(elements, flags) } catch (e) { if (e instanceof Error && e.message.includes('Missing Focus.')) { focusIn(elements, Focus.First) } else throw e }

Prevention

When it happens

Trigger: Calling focusIn(elements, Focus.NoScroll) with no directional bit; passing 0 or an undefined `focus` argument; building a custom flag mask that omits First/Previous/Next/Last; internal components invoked with a corrupted Focus enum (e.g. tree-shaking or duplicate @headlessui versions mangling the enum values).

Common situations: Custom widgets copying Headless UI internals; passing a focus option read from user input/config that defaults to 0; mixing two different @headlessui-react versions in a monorepo so the Focus enum values from one copy don't match the other's.

Related errors


AI-assisted analysis of tailwindlabs/headlessui@eea57cf46f (2026-08-28). Data as JSON: /api/errors/17cc051b78b513ce. Report an issue: GitHub.