tailwindlabs/headlessui · error · Error
Missing parent
Error message
Missing parent
What it means
In @headlessui-vue, Description-related composables inject a DescriptionContext that is provided by a parent component (Dialog, Combobox, Listbox, RadioGroup, etc.). useDescriptionContext() throws 'Missing parent' when inject() returns null, meaning the Description is being rendered outside any component that provides the description context.
Solutions
- Move the <Description> (or code using useDescriptions) inside the parent widget that provides the context, e.g. inside <Dialog>...</Dialog>.
- If you need a custom component to host the Description, ensure it is rendered as a descendant of the providing component so Vue's provide/inject can resolve.
- Check that the parent is the matching headlessui component version (mixed versions can fail to provide context).
Example fix
// before <Description>Read-only input</Description> <Combobox v-model="x">...</Combobox> // after <Combobox v-model="x"> ... <Description>Read-only input</Description> </Combobox>
Defensive patterns
Strategy: validation
Validate before calling
// In Vue: ensure the Description is a descendant of a providing widget before rendering it
<Dialog :open="open">
<!-- only render Description here, inside the provider -->
<Description>{{ text }}</Description>
</Dialog> Try / catch
try { useDescriptions() } catch (e) { if (e instanceof Error && e.message === 'Missing parent') { /* fall back to a plain <p> with id and manual aria-describedby */ } else throw e } Prevention
- Keep <Description> markup nested inside the owning widget (Dialog/Combobox/Listbox/RadioGroup).
- When extracting Description into its own component, import and place it within the parent's default slot.
- Review template structure after refactors to confirm provide/inject ancestry is intact.
When it happens
Trigger: Rendering <Description> as a standalone component not nested inside <Dialog>, <Combobox>, <Listbox>, or another providing parent; moving Description markup out of the parent during refactor; rendering it in a Teleport/portal that escapes the provide/inject tree in some setups.
Common situations: Extracting description text into a separate component file and forgetting it must stay inside the widget; restructuring templates so the Description ends up a sibling instead of a descendant of the provider component.
Related errors
- A <TransitionChild /> is used but it is missing a parent…
- A <DialogBackdrop /> component is being used, but a…
- A <Transition.Child /> is used but it is missing a parent…
- A <Transition /> is used but it is missing a `:show="true |…
- Did you forget to passthrough the `ref` to the actual DOM…
AI-assisted analysis of tailwindlabs/headlessui@eea57cf46f (2026-08-28).
Data as JSON: /api/errors/c84005b92c8f1f2b.
Report an issue: GitHub.
Appendix: source
Thrown at packages/@headlessui-vue/src/components/description/description.ts:29
type InjectionKey,
type Ref,
} from 'vue'
import { useId } from '../../hooks/use-id'
import { render } from '../../utils/render'
// ---
let DescriptionContext = Symbol('DescriptionContext') as InjectionKey<{
register(value: string): () => void
slot: Ref<Record<string, any>>
name: string
props: Record<string, any>
}>
function useDescriptionContext() {
let context = inject(DescriptionContext, null)
if (context === null) {
throw new Error('Missing parent')
}
return context
}
export function useDescriptions({
slot = ref({}),
name = 'Description',
props = {},
}: {
slot?: Ref<Record<string, unknown>>
name?: string
props?: Record<string, unknown>
} = {}): ComputedRef<string | undefined> {
let descriptionIds = ref<string[]>([])
function register(value: string) {
descriptionIds.value.push(value)
View on GitHub (pinned to eea57cf46f)