gitbutlerapp/gitbutler · error · Error

Expected to find content container

Error message

Expected to find content container

What it means

setupViewport assumes the Scrollbar component's viewport element has a child element (the content container) at index 0, using it to track content size for thumb positioning. When the viewport has no children at setup time the component throws this error instead of rendering a scrollbar that can never be sized correctly.

Solutions

  1. Ensure the viewport always contains its content wrapper before Scrollbar binds (render content inside the viewport slot).
  2. Defer Scrollbar setup until the content element exists (await tick() / mount after data loads).
  3. Patch setupViewport to defer or re-check with a MutationObserver instead of throwing when children is empty.
  4. In tests, always render a child element inside the viewport.
  5. Verify the element passed as viewport is the scrolling container, not the content itself.

Example fix

// before
const content = viewport.children.item(0);
if (!content) throw new Error("Expected to find content container");
// after
const content = viewport.children.item(0);
if (!content) {
  new MutationObserver((_m, obs) => {
    if (viewport.children.item(0)) { obs.disconnect(); setupViewport(viewport); }
  }).observe(viewport, { childList: true });
  return;
}
Defensive patterns

Strategy: validation

Validate before calling

// before wiring Scrollbar
if (!viewport || viewport.children.length === 0) {
  await tick(); // or wait for content render
  if (viewport.children.length === 0) return; // skip scrollbar entirely
}

Type guard

function hasContentContainer(el: Element): boolean {
  return el.children.length > 0 && el.children.item(0) !== null;
}

Try / catch

try {
  setupViewport(viewport);
} catch (err) {
  if (err instanceof Error && err.message === "Expected to find content container") {
    observeUntilChild(viewport, () => setupViewport(viewport)); // retry on DOM mutation
  } else throw err;
}

Prevention

When it happens

Trigger: Scrollbar is attached to a viewport element that has no child elements yet — e.g. the slot/content is conditionally rendered after mount, the content is removed before setup runs, or the component is wired to the wrong (empty) element.

Common situations: Async-loaded list content not yet in the DOM when Scrollbar initializes; {#if} blocks hiding the content initially; tests mounting Scrollbar without slot children; refactoring that changed the DOM nesting so viewport.children[0] no longer exists.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/abc8fb3479d5460f. Report an issue: GitHub.

Appendix: source

Thrown at packages/ui/src/lib/components/scroll/Scrollbar.svelte:158

		if (!shouldShowOnHover) return;
		visible = false;
	}

	function setupViewport(viewport: Element) {
		if (!viewport) return;

		if (typeof window.ResizeObserver === "undefined") {
			throw new Error("window.ResizeObserver is missing.");
		}

		const observerSize = new ResizeObserver(() => {
			updateTrack();
		});
		observerSize.observe(viewport);

		const content = viewport.children.item(0);
		if (!content) {
			throw new Error("Expected to find content container");
		}

		// Sometimes the content size changes before the browser fires a
		// scroll event, so we compensate here to avoid thumb jumpiness.
		// However, if viewport.scrollTop has already been adjusted by
		// another component (e.g. VirtualList's scroll compensation),
		// we must use the actual value to avoid double-compensating.
		const observerContentSize = new ResizeObserver(() => {
			if (lastHeight) {
				const diff = content.scrollHeight - lastHeight;
				// If viewport.scrollTop diverged from our last-known value,
				// another component (e.g. VirtualList) already compensated.
				if (viewport.scrollTop !== scrollTop) {
					scrollTop = viewport.scrollTop;
				} else if (diff !== 0) {
					scrollTop = viewport.scrollTop + diff;
				}
			}

View on GitHub (pinned to 58e5313667)