d2lang/d2 · error
failed to close Playwright browser: %w
Error message
failed to close Playwright browser: %w
What it means
RestartBrowser tears down the current Playwright browser before starting a new one. If pw.Browser.Close() returns an error, the restart aborts with this wrapped error instead of proceeding to start a replacement browser. This usually indicates the browser process is already dead or wedged.
Source
Thrown at lib/png/png.go:34
"github.com/mxschmitt/playwright-go"
"github.com/d2lang/d2/lib/compression"
"github.com/d2lang/d2/lib/env"
"github.com/d2lang/d2/lib/version"
)
// ConvertSVG scales the image by 2x
const SCALE = 2.
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{View on GitHub (pinned to 0d69dca6f5)
Solutions
- Check whether the browser process crashed (logs/OOM killer) and address the resource cause before restarting
- If the browser is already closed/dead, treat Close's error as non-fatal and proceed with startPlaywright directly
- Ensure RestartBrowser isn't called concurrently with Cleanup or itself; serialize lifecycle calls with a mutex
- Verify Playwright driver and browser versions are compatible; reinstall browsers if the install is corrupted
Example fix
// before
if err := pw.Browser.Close(); err != nil {
return Playwright{}, fmt.Errorf("failed to close Playwright browser: %w", err)
}
// after
if err := pw.Browser.Close(); err != nil {
// browser likely already dead; continue with restart
log.Printf("browser close failed (ignoring): %v", err)
} Defensive patterns
Strategy: try-catch
Try / catch
if _, err := pw.RestartBrowser(); err != nil {
if strings.Contains(err.Error(), "failed to close Playwright browser") {
// browser already dead — call startPlaywright directly instead of failing
}
} Prevention
- Serialize all browser lifecycle calls (RestartBrowser/Cleanup) behind a mutex
- Monitor for Chromium OOM kills; provision adequate memory
- Never call RestartBrowser after Cleanup has run
- Treat Close errors on an already-dead browser as non-fatal
When it happens
Trigger: Browser.Close() fails during RestartBrowser — typically the Chromium process already crashed or was killed (OOM, SIGKILL), the driver lost the connection, or Close is called on an already-closed browser.
Common situations: Long-running bundler workers where Chromium crashed under memory pressure; concurrent Close calls from multiple goroutines; calling RestartBrowser after Cleanup already stopped everything.
Related errors
- failed to start new Playwright page: %w
- issue encountered with PNG exporter: %w
- failed to stop Playwright: %w
- failed to launch Chromium: %w
- failed to start new Playwright browser context: %w
AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31).
Data as JSON: /api/errors/e1198b9ad0cff6b7.
Report an issue: GitHub.