gitbutlerapp/gitbutler · error · Error

window.ResizeObserver is missing.

Error message

window.ResizeObserver is missing.

What it means

Scrollbar.svelte's setupViewport requires window.ResizeObserver to observe the viewport element's size changes and keep the scrollbar thumb in sync. If ResizeObserver is undefined the component throws immediately during viewport setup rather than silently producing a broken scrollbar.

Solutions

  1. Add a ResizeObserver polyfill (e.g. resize-observer-polyfill) to the app entry point.
  2. Upgrade or configure the target browser/webview to one supporting ResizeObserver.
  3. In tests, stub global.ResizeObserver with a no-op class before mounting the component.
  4. Alternatively, patch Scrollbar.svelte to fall back to window resize + interval polling when the API is absent.
  5. Feature-detect before mounting Scrollbar and skip custom scrollbars on unsupported platforms.

Example fix

// before (test setup)
import { render } from "@testing-library/svelte";
// after
global.ResizeObserver = global.ResizeObserver || class { observe(){} unobserve(){} disconnect(){} };
import { render } from "@testing-library/svelte";
Defensive patterns

Strategy: fallback

Validate before calling

if (typeof window === "undefined" || typeof window.ResizeObserver === "undefined") {
  // skip custom scrollbar or load polyfill first
  import("resize-observer-polyfill").then((RO) => { (window as any).ResizeObserver = RO.default; });
}

Type guard

function hasResizeObserver(w: Window): w is Window & { ResizeObserver: typeof ResizeObserver } {
  return typeof (w as any).ResizeObserver === "function";
}

Try / catch

try {
  mountScrollbar(viewport);
} catch (err) {
  if (err instanceof Error && err.message.includes("ResizeObserver")) {
    mountNativeScrollbar(viewport); // browser default scrolling
  } else throw err;
}

Prevention

When it happens

Trigger: The Scrollbar component is mounted in an environment without ResizeObserver: very old browsers (IE11, old Safari <13.4), non-browser jsdom-based test environments without the polyfill, or sandboxed webviews lacking the API.

Common situations: Unit tests rendering Svelte components in jsdom without a ResizeObserver polyfill; legacy embedded browsers; SSR/hydration setups where window is stubbed incompletely.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


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

Appendix: source

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

	/////////////////////
	// VIEWPORT EVENTS //
	/////////////////////
	function onViewportMouseEnter() {
		if (!shouldShowOnHover) return;
		visible = true;
	}

	function onViewportMouseLeave() {
		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(() => {

View on GitHub (pinned to 58e5313667)