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 = false

View on GitHub (pinned to e474e8803c)

Solutions

  1. Ensure the target element is rendered before (or guaranteed with) the Portal — move Portal inside the same {#if} block or use a delay
  2. Pass an HTMLElement reference instead of a selector to avoid query timing issues
  3. Fix the selector typo / use a stable id
  4. 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

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


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/3eb0ffbfcdd72bc8. Report an issue: GitHub.