{"record":{"id":"e1198b9ad0cff6b7","repo":"d2lang/d2","slug":"failed-to-close-playwright-browser-w","errorCode":null,"errorMessage":"failed to close Playwright browser: %w","messagePattern":"failed to close Playwright browser: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/png/png.go","lineNumber":34,"sourceCode":"\t\"github.com/mxschmitt/playwright-go\"\n\n\t\"github.com/d2lang/d2/lib/compression\"\n\t\"github.com/d2lang/d2/lib/env\"\n\t\"github.com/d2lang/d2/lib/version\"\n)\n\n// ConvertSVG scales the image by 2x\nconst SCALE = 2.\n\ntype Playwright struct {\n\tPW      *playwright.Playwright\n\tBrowser playwright.Browser\n\tPage    playwright.Page\n}\n\nfunc (pw *Playwright) RestartBrowser() (Playwright, error) {\n\tif err := pw.Browser.Close(); err != nil {\n\t\treturn Playwright{}, fmt.Errorf(\"failed to close Playwright browser: %w\", err)\n\t}\n\treturn startPlaywright(pw.PW)\n}\n\nfunc (pw *Playwright) Cleanup() error {\n\tif err := pw.Browser.Close(); err != nil {\n\t\treturn fmt.Errorf(\"failed to close Playwright browser: %w\", err)\n\t}\n\tif err := pw.PW.Stop(); err != nil {\n\t\treturn fmt.Errorf(\"failed to stop Playwright: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc startPlaywright(pw *playwright.Playwright) (Playwright, error) {\n\t// Optimizations for a very tightly scoped Playwright instance\n\tbrowser, err := pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{\n\t\tArgs: []string{","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/lib/png/png.go#L16-L52","documentation":"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.","triggerScenarios":"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.","commonSituations":"Long-running bundler workers where Chromium crashed under memory pressure; concurrent Close calls from multiple goroutines; calling RestartBrowser after Cleanup already stopped everything.","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"],"exampleFix":"// before\nif err := pw.Browser.Close(); err != nil {\n\treturn Playwright{}, fmt.Errorf(\"failed to close Playwright browser: %w\", err)\n}\n// after\nif err := pw.Browser.Close(); err != nil {\n\t// browser likely already dead; continue with restart\n\tlog.Printf(\"browser close failed (ignoring): %v\", err)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, err := pw.RestartBrowser(); err != nil {\n\tif strings.Contains(err.Error(), \"failed to close Playwright browser\") {\n\t\t// browser already dead — call startPlaywright directly instead of failing\n\t}\n}","preventionTips":["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"],"tags":["playwright","browser","lifecycle","go"],"backgroundTag":"browser-close-failed","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}