{"record":{"id":"3eb0ffbfcdd72bc8","repo":"windmill-labs/windmill","slug":"no-element-found-matching-css-selector-target","errorCode":null,"errorMessage":"No element found matching css selector: \"${target}\"","messagePattern":"No element found matching css selector: \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/Portal.svelte","lineNumber":16,"sourceCode":"<script module>\n\timport { tick } from 'svelte'\n\n\texport function portal(el, options) {\n\t\tlet { target, name } = options\n\t\tlet targetEl\n\t\tasync function update(newTarget) {\n\t\t\ttarget = newTarget\n\t\t\tif (typeof target === 'string') {\n\t\t\t\ttargetEl = document.querySelector(target)\n\t\t\t\tif (targetEl === null) {\n\t\t\t\t\tawait tick()\n\t\t\t\t\ttargetEl = document.querySelector(target)\n\t\t\t\t}\n\t\t\t\tif (targetEl === null) {\n\t\t\t\t\tthrow new Error(`No element found matching css selector: \"${target}\"`)\n\t\t\t\t}\n\t\t\t} else if (target instanceof HTMLElement) {\n\t\t\t\ttargetEl = target\n\t\t\t} else {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t`Unknown portal target type: ${\n\t\t\t\t\t\ttarget === null ? 'null' : typeof target\n\t\t\t\t\t}. Allowed types: string (CSS selector) or HTMLElement.`\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (!el.classList.contains('windmill-app')) {\n\t\t\t\tel.classList.add('windmill-app')\n\t\t\t}\n\t\t\tif (name && !el.classList.contains(name)) {\n\t\t\t\tel.classList.add(name)\n\t\t\t}\n\t\t\ttargetEl.appendChild(el)\n\t\t\tel.hidden = false","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/Portal.svelte#L1-L34","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n<Portal target=\"#app-modal\">...</Portal>\n// after\n{#if mounted}\n  <Portal target=\"#app-modal\">...</Portal>\n{/if}","handlingStrategy":"validation","validationCode":"if (typeof target === 'string' && !document.querySelector(target)) {\n  // defer rendering the Portal until the target exists\n  await tick()\n}","typeGuard":"function targetElExists(target: string | HTMLElement): boolean {\n  return target instanceof HTMLElement || document.querySelector(target) !== null\n}","tryCatchPattern":"try {\n  renderPortal(target)\n} catch (e) {\n  if (String(e.message).startsWith('No element found matching css selector')) {\n    console.warn('Portal target missing, skipping render:', target)\n  } else throw e\n}","preventionTips":["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"],"tags":["dom","svelte","portal","selector"],"backgroundTag":"dom-element-not-found","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}