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
- Coerce to boolean at the call site: `open={Boolean(isOpen)}` or `open={!!isOpen}`.
- Initialize state properly: `useState<boolean>(false)`, not `useState(null)`.
- 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
- Type state as boolean with useState<boolean>(false).
- Never pass optional-chained or nullable values without !! coercion.
- Use braces {} not strings for boolean JSX props.
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
- You have to provide an `open` and an `onClose` prop to the `
- You provided an `onClose` prop to the `Dialog`, but forgot a
- You provided an `open` prop to the `Dialog`, but forgot an `
- You provided an `onClose` prop to the `Dialog`, but the valu
- Invalid role [${role}] passed to <Dialog />. Only `dialog` a
AI-assisted analysis of tailwindlabs/headlessui@eea57cf46f (2026-08-28).
Data as JSON: /api/errors/a45465abbb11b6b9.
Report an issue: GitHub.