GopeedLab/gopeed · error
webview startup timeout
Error message
webview startup timeout
What it means
pageWrapper.start() spawns the native webview (webview.New or NewHeadless), binds the callback, sets bootstrap HTML, then waits on the ready channel with a hard 10-second budget. If the runtime never signals ready within that window, this error returns and the page is unusable. The tracef call just before it records how many ms actually elapsed.
Source
Thrown at internal/webview/goprovider/provider.go:147
applyWindowOptions(w, p.opts)
w.SetHtml(buildBootstrapHTML(p.callbackName, p.readyID))
w.Run()
w.Destroy()
close(p.done)
}
if !postMainThreadTask(run) {
go run()
}
select {
case err := <-p.ready:
p.tracef("open %dms", time.Since(startedAt).Milliseconds())
return err
case <-time.After(10 * time.Second):
p.tracef("open timeout %dms", time.Since(startedAt).Milliseconds())
return fmt.Errorf("webview startup timeout")
}
}
func (p *pageWrapper) AddInitScript(script string) error {
return p.dispatch(func(w webview.WebView) error {
w.Init(script)
return nil
})
}
func (p *pageWrapper) Goto(url string, opts enginewebview.GotoOptions) error {
startedAt := time.Now()
p.drainLoads()
if err := p.dispatch(func(w webview.WebView) error {
p.mu.Lock()
p.url = url
p.mu.Unlock()
w.Navigate(url)View on GitHub (pinned to 7b7327ffb3)
Solutions
- Set the Headless option when there is no display (start() then uses webview.NewHeadless)
- On Linux install WebKitGTK dev/runtime packages; on Windows confirm the WebView2 runtime is installed
- Ensure DISPLAY/WAYLAND_DISPLAY are set in remote sessions, or forward X
- Check the provider trace logs (title containing the debug marker) to see whether open hung at 0ms or crept to 10000ms — a 0ms hang points at a blocked task queue, a near-10000ms value at slow init
Example fix
// before
page, err := provider.NewPage(enginewebview.BrowserOptions{}) // in CI: no display -> startup timeout
// after
page, err := provider.NewPage(enginewebview.BrowserOptions{Headless: true}) // headless init path Defensive patterns
Strategy: retry
Validate before calling
// Check the environment before opening a webview
func canOpenGUI() bool {
if os.Getenv("Headless") != "" { /* app-level headless flag */ }
return os.Getenv("DISPLAY") != "" || os.Getenv("WAYLAND_DISPLAY") != "" || runtime.GOOS == "windows" || runtime.GOOS == "darwin"
} Try / catch
page, err := provider.NewPage(opts)
if err != nil {
if strings.Contains(err.Error(), "webview startup timeout") {
// one cold-start retry, then abort the session; do not loop on a broken environment
page, err = provider.NewPage(opts)
if err != nil {
return fmt.Errorf("webview unavailable in this environment: %w", err)
}
} else {
return err
}
} Prevention
- Set Headless in CI/containers; require a display for windowed mode
- Pre-install the webview runtime (WebView2 on Windows, WebKitGTK on Linux)
- Keep the main thread free of other blocking work while a webview boots
When it happens
Trigger: Running a visible webview where no GUI session exists (no DISPLAY / Wayland session, e.g. plain SSH or a container) without Headless set; missing WebView2 runtime on Windows or WebKitGTK shared libraries on Linux; the main-thread task queue blocked by another long-running window; extremely slow cold start under heavy load.
Common situations: CI pipelines or servers invoking webview-dependent features without headless mode; minimal Docker images lacking libwebkit2gtk; first launch after an OS update while the webview runtime rebuilds its cache; multiple providers competing for the one UI thread.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16).
Data as JSON: /api/errors/8bfe16c10c52059e.
Report an issue: GitHub.