tailwindlabs/headlessui · error · Error

You provided an `open` prop to the `Dialog`, but the value i

Error message

You provided an `open` prop to the `Dialog`, but the value is not a boolean. Received: ${props.open}

What it means

After confirming `open`/`onClose` presence, Dialog validates types: unless an OpenClosed context provides state, `props.open` must be a strict boolean. Anything else (string 'true', number, null, undefined, object) throws with the received value interpolated, since truthy-based logic would break the controlled contract.

Source

Thrown at packages/@headlessui-react/src/components/dialog/dialog.tsx:405

    throw new Error(
      `You have to provide an \`open\` and an \`onClose\` prop to the \`Dialog\` component.`
    )
  }

  if (!hasOpen) {
    throw new Error(
      `You provided an \`onClose\` prop to the \`Dialog\`, but forgot an \`open\` prop.`
    )
  }

  if (!hasOnClose) {
    throw new Error(
      `You provided an \`open\` prop to the \`Dialog\`, but forgot an \`onClose\` prop.`
    )
  }

  if (!usesOpenClosedState && typeof props.open !== 'boolean') {
    throw new Error(
      `You provided an \`open\` prop to the \`Dialog\`, but the value is not a boolean. Received: ${props.open}`
    )
  }

  if (typeof props.onClose !== 'function') {
    throw new Error(
      `You provided an \`onClose\` prop to the \`Dialog\`, but the value is not a function. Received: ${props.onClose}`
    )
  }

  if ((open !== undefined || transition) && !rest.static) {
    return (
      <MainTreeProvider>
        <Transition show={open} transition={transition} unmount={rest.unmount}>
          <InternalDialog ref={ref} {...rest} />
        </Transition>
      </MainTreeProvider>
    )

View on GitHub (pinned to eea57cf46f)

Solutions

  1. Coerce to boolean at the call site: `open={Boolean(isOpen)}` or `open={!!isOpen}`.
  2. Initialize state properly: `useState<boolean>(false)`, not `useState(null)`.
  3. Guard async values: render the Dialog only when the value is defined, or default it: `open={open ?? false}`.

Example fix

// before
<Dialog open={user?.hasSeenIntro} onClose={close}>

// after
<Dialog open={Boolean(user?.hasSeenIntro)} onClose={close}>
Defensive patterns

Strategy: type-guard

Validate before calling

<Dialog open={Boolean(open)} onClose={close}>

Type guard

const isOpenBoolean = (v: unknown): v is boolean => typeof v === 'boolean'

Prevention

When it happens

Trigger: `<Dialog open="false">` (string); `open={user?.isActive}` where the value is undefined; `open={1}`; `open={status}` where status is an enum/string; binding to a nullable async value.

Common situations: JSX string attributes instead of braces; optional-chained values that are undefined before data loads; truthy APIs (counts, objects) used as booleans; state initialized to null.

Related errors


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