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: ${open.value === Missing ? undefined : props.open}

What it means

After confirming an open state exists, the Vue Dialog validates that open is strictly a boolean. Because Vue props are flexible, stringly-typed or numeric values (e.g. open="false") would break the component's internal watchers, so setup() throws with the received value to surface the mistake early.

Source

Thrown at packages/@headlessui-vue/src/components/dialog/dialog.ts:126

      }
      return props.open
    })

    let internalDialogRef = ref<HTMLDivElement | null>(null)

    let ownerDocument = computed(() => getOwnerDocument(internalDialogRef))

    expose({ el: internalDialogRef, $el: internalDialogRef })

    // Validations
    let hasOpen = props.open !== Missing || usesOpenClosedState !== null

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

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

    let dialogState = computed(() =>
      !ready.value ? DialogStates.Closed : open.value ? DialogStates.Open : DialogStates.Closed
    )
    let enabled = computed(() => dialogState.value === DialogStates.Open)

    let hasNestedDialogs = computed(() => nestedDialogCount.value > 1) // 1 is the current dialog
    let hasParentDialog = inject(DialogContext, null) !== null
    let [portals, PortalWrapper] = useNestedPortals()
    let {
      resolveContainers: resolveRootContainers,
      mainTreeNodeRef,
      MainTreeNode,

View on GitHub (pinned to eea57cf46f)

Solutions

  1. Use a binding with a real boolean: :open="isOpen" (ensure isOpen is a boolean ref).
  2. Convert non-boolean state: :open="Boolean(value)" or normalize the value at its source (store/localStorage/route param).
  3. For static usage, write :open="true" not open="true".

Example fix

// before
<Dialog open="false" @close="...">...</Dialog> <!-- string "false" -->

// after
<Dialog :open="false" @close="...">...</Dialog>
Defensive patterns

Strategy: type-guard

Validate before calling

// Normalize before binding
<Dialog :open="typeof rawOpen === 'boolean' ? rawOpen : rawOpen === 'true'" @close="...">

Type guard

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

Prevention

When it happens

Trigger: Passing open="true" / open="false" as a static string attribute (no v-bind) so Vue delivers a string; binding open to a number (0/1), null, undefined, or a non-boolean ref; passing a value from a URL query param or form input without conversion.

Common situations: Writing open="false" instead of :open="false"; storing dialog state as 0/1 or 'yes'/'no' in a store; binding to localStorage or route query values that come back as strings.

Related errors


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