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
- Use a binding with a real boolean: :open="isOpen" (ensure isOpen is a boolean ref).
- Convert non-boolean state: :open="Boolean(value)" or normalize the value at its source (store/localStorage/route param).
- 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
- Always bind open with : (v-bind), never as a static attribute.
- Keep dialog state as a boolean ref (ref(false)) rather than 0/1 or strings.
- Convert values from localStorage, query params, or APIs with Boolean(...) before passing them in.
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
- You forgot to provide an `open` prop to the `Dialog`.
- A <DialogBackdrop /> component is being used, but a <DialogP
- Invalid role [${role}] passed to <Dialog />. Only `dialog` a
- There are no focusable elements inside the <FocusTrap />
- Missing parent
AI-assisted analysis of tailwindlabs/headlessui@eea57cf46f (2026-08-28).
Data as JSON: /api/errors/3559af6483b35095.
Report an issue: GitHub.