windmill-labs/windmill · error · Error
No element found matching css selector: "${target}"
Error message
No element found matching css selector: "${target}" What it means
The Portal component renders its children into another DOM node identified by a CSS selector. In its update action it queries the selector, waits one tick and retries, and throws if the element still does not exist — guarding against rendering into nothing.
Source
Thrown at frontend/src/lib/components/Portal.svelte:16
<script module>
import { tick } from 'svelte'
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 = falseView on GitHub (pinned to e474e8803c)
Solutions
- Ensure the target element is rendered before (or guaranteed with) the Portal — move Portal inside the same {#if} block or use a delay
- Pass an HTMLElement reference instead of a selector to avoid query timing issues
- Fix the selector typo / use a stable id
- Use bind:this on the target element and pass the bound variable as target
Example fix
// before
<Portal target="#app-modal">...</Portal>
// after
{#if mounted}
<Portal target="#app-modal">...</Portal>
{/if} Defensive patterns
Strategy: validation
Validate before calling
if (typeof target === 'string' && !document.querySelector(target)) {
// defer rendering the Portal until the target exists
await tick()
} Type guard
function targetElExists(target: string | HTMLElement): boolean {
return target instanceof HTMLElement || document.querySelector(target) !== null
} Try / catch
try {
renderPortal(target)
} catch (e) {
if (String(e.message).startsWith('No element found matching css selector')) {
console.warn('Portal target missing, skipping render:', target)
} else throw e
} Prevention
- Render <Portal> only after the target element is mounted (same {#if} block or onMount)
- Prefer bind:this HTMLElement references over CSS selectors
- Use stable, unique ids for portal targets
- Double-check selector spelling
When it happens
Trigger: Using <Portal target="#some-id"> where no element matches `#some-id` at mount time nor after one tick — the target element is not yet rendered, is conditionally removed, or the selector is mistyped.
Common situations: Portaling into a modal/container that renders after the Portal (mount-order race); target inside an {#if} block that is currently false; typo in the selector; target removed on route change.
Related errors
- Unknown portal target type: ${target === null ? 'null' : typ
- 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/3eb0ffbfcdd72bc8.
Report an issue: GitHub.