Skip to content

Portal cleanup throws if mount was externally cleared (1.x and 2.0.0-beta-14) #2775

Description

@yumemi-thomas

Describe the bug

Disposing a <Portal> throws if the portal mount was externally cleared before Solid cleanup runs. This reproduces in Solid 1.x and still reproduces on Solid 2 next.

Your Example Website or App

https://stackblitz.com/edit/solidjs-templates-wvzy8wgj?file=src%2FApp.tsx

import { createSignal, Show, flush } from 'solid-js';
import { Portal } from '@solidjs/web';

const externalClearMount = document.createElement('div');
document.body.append(externalClearMount);

export default function App() {
  const [open, setOpen] = createSignal(true);
  const [result, setResult] = createSignal('not run');

  return (
    <main>
      <button
        onClick={() => {
          try {
            externalClearMount.textContent = '';
            setOpen(false);
            flush();
            setResult('disposed without throwing');
          } catch (error) {
            setResult(String(error));
          }
        }}
      >
        clear mount then close portal
      </button>

      <p>{result()}</p>

      <Show when={open()}>
        <Portal mount={externalClearMount}>
          <span>overlay</span>
        </Portal>
      </Show>
    </main>
  );
}

Steps to Reproduce the Bug or Issue

  1. Open the StackBlitz repro.
  2. Click clear mount then close portal.
  3. Observe the rendered result.
NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

Expected behavior

If the mount was already externally cleared, dispose() should not throw.

Expected result:

disposed without throwing

Screenshots or Videos

No response

Platform

  • OS: macOS
  • Browser: Chrome
  • Version: Solid 1.x and Solid 2 next

Additional context

Observed in Solid 1.x and on current Solid 2 next at d8921ac1:

  • Closing after mount.textContent = "" throws NotFoundError.

This is an edge case involving external DOM mutation, but Portal mount={...} is specifically for rendering into a DOM node outside the normal component tree. Integration hosts, third-party modal containers, tests, or shell apps can realistically empty that container before Solid disposes the Portal.

This appears related to Portal's marker-range cleanup. Cleanup calls removeChild without guarding whether the child is still in the mount.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions