windmill-labs/windmill · error · TypeError
Unknown portal target type: ${target === null ? 'null' : typ
Error message
Unknown portal target type: ${target === null ? 'null' : typeof target}. Allowed types: string (CSS selector) or HTMLElement. What it means
Portal's target prop must be either a CSS selector string or an HTMLElement. update() throws a TypeError when target is anything else (null, undefined, number, object), because there is no valid DOM node to portal into.
Source
Thrown at frontend/src/lib/components/Portal.svelte:21
export function portal(el, options) {
let { target, name } = options
let targetEl
async function update(newTarget) {
target = newTarget
if (typeof target === 'string') {
targetEl = document.querySelector(target)
if (targetEl === null) {
await tick()
targetEl = document.querySelector(target)
}
if (targetEl === null) {
throw new Error(`No element found matching css selector: "${target}"`)
}
} else if (target instanceof HTMLElement) {
targetEl = target
} else {
throw new TypeError(
`Unknown portal target type: ${
target === null ? 'null' : typeof target
}. Allowed types: string (CSS selector) or HTMLElement.`
)
}
if (!el.classList.contains('windmill-app')) {
el.classList.add('windmill-app')
}
if (name && !el.classList.contains(name)) {
el.classList.add(name)
}
targetEl.appendChild(el)
el.hidden = false
}
function destroy() {
if (el.parentNode) {
el.parentNode.removeChild(el)View on GitHub (pinned to e474e8803c)
Solutions
- Only render <Portal> once the target variable is a string or HTMLElement (guard with {#if})
- Pass an HTMLElement via bind:this instead of a nullable lookup result
- Provide a default selector string, e.g. 'body'
- Fix the type of the value being passed — it must be string | HTMLElement
Example fix
// before
<Portal target={maybeEl}>...</Portal>
// after
{#if typeof maybeTarget === 'string' || maybeTarget instanceof HTMLElement}
<Portal target={maybeTarget}>...</Portal>
{/if} Defensive patterns
Strategy: type-guard
Validate before calling
const validTarget = typeof target === 'string' || target instanceof HTMLElement if (!validTarget) target = 'body' // safe default
Type guard
function isPortalTarget(t: unknown): t is string | HTMLElement {
return typeof t === 'string' || t instanceof HTMLElement
} Try / catch
try {
renderPortal(target)
} catch (e) {
if (e instanceof TypeError && e.message.startsWith('Unknown portal target type')) {
console.warn('Bad portal target, defaulting to body', target)
renderPortal('body')
} else throw e
} Prevention
- Guard async-loaded target values with {#if} before rendering <Portal>
- Never pass nullable lookup results directly as target
- Type the target prop explicitly as string | HTMLElement | undefined in wrappers
When it happens
Trigger: <Portal target={undefined}> because a variable is not yet assigned; target={null} from a failed lookup; passing a non-DOM object (e.g. a Svelte store value or config object) as target.
Common situations: Binding target to data that loads asynchronously so it is undefined at first render; refactoring from string selectors to elements (or vice versa) and leaving a wrong type; a parent component passing an optional prop straight through.
Related errors
- No element found matching css selector: "${target}"
- PlanWriteRefusedError
- Can't create script from non-inline script
- no first step found
- only scripts can be used as a input schema
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/8224b9dc1731fcd6.
Report an issue: GitHub.