d2lang/d2 · warning

failed to stop Playwright: %w

Error message

failed to stop Playwright: %w

What it means

Cleanup, after successfully closing the browser, stops the Playwright driver process with pw.PW.Stop(). If that fails it returns this wrapped error. The driver (node process managing browsers) could not be terminated, potentially leaving orphaned node/Chromium processes.

Source

Thrown at lib/png/png.go:44

type Playwright struct {
	PW      *playwright.Playwright
	Browser playwright.Browser
	Page    playwright.Page
}

func (pw *Playwright) RestartBrowser() (Playwright, error) {
	if err := pw.Browser.Close(); err != nil {
		return Playwright{}, fmt.Errorf("failed to close Playwright browser: %w", err)
	}
	return startPlaywright(pw.PW)
}

func (pw *Playwright) Cleanup() error {
	if err := pw.Browser.Close(); err != nil {
		return fmt.Errorf("failed to close Playwright browser: %w", err)
	}
	if err := pw.PW.Stop(); err != nil {
		return fmt.Errorf("failed to stop Playwright: %w", err)
	}
	return nil
}

func startPlaywright(pw *playwright.Playwright) (Playwright, error) {
	// Optimizations for a very tightly scoped Playwright instance
	browser, err := pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{
		Args: []string{
			"--no-sandbox",                             // Removes security overhead
			"--disable-dev-shm-usage",                  // Prevents /dev/shm issues
			"--disable-background-timer-throttling",    // Prevents CPU throttling
			"--disable-backgrounding-occluded-windows", // Keeps rendering active
			"--disable-features=TranslateUI",           // Reduces feature overhead
			"--disable-ipc-flooding-protection",        // Removes IPC limits
		},
	})
	if err != nil {
		return Playwright{}, fmt.Errorf("failed to launch Chromium: %w", err)

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Check for orphaned node/chromium processes after the error and kill them manually if needed
  2. Make Cleanup idempotent and single-run (sync.Once) to avoid racing Stop against itself
  3. Log and continue: Stop failure at process exit is rarely fatal, so don't mask the original operation's result with it
  4. Upgrade playwright-go to the latest version — driver lifecycle bugs are fixed regularly

Example fix

// before
if err := pw.PW.Stop(); err != nil {
	return fmt.Errorf("failed to stop Playwright: %w", err)
}
// after
if err := pw.PW.Stop(); err != nil {
	log.Printf("playwright stop failed (driver may have already exited): %v", err)
}
Defensive patterns

Strategy: try-catch

Try / catch

if err := pw.Cleanup(); err != nil {
	if strings.Contains(err.Error(), "failed to stop Playwright") {
		// check for orphaned driver processes; don't mask the original error
	}
}

Prevention

When it happens

Trigger: pw.PW.Stop() returns an error during Cleanup — driver process already exited (e.g. after a browser crash), broken pipe to the driver, or Stop racing a concurrent Cleanup.

Common situations: Shutdown after Chromium crashed taking the driver with it; SIGTERM handling that calls Cleanup while the driver is mid-operation; test suites leaking drivers when this path fails.

Related errors


AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31). Data as JSON: /api/errors/ec42647d3c85b41f. Report an issue: GitHub.