wavetermdev/waveterm · warning
error bootstrapping layout, no windows exist
Error message
error bootstrapping layout, no windows exist
What it means
BootstrapStarterLayout requires at least one window on the Client record because the starter layout is applied to the active tab of the first window. This error is returned directly (no wrapped cause) when client.WindowIds is empty — a sentinel condition, not a DB failure.
Source
Thrown at pkg/wcore/layout.go:154
err := QueueLayoutActionForTab(ctx, tabId, actions...)
if err != nil {
return fmt.Errorf("unable to queue layout actions for portable layout: %w", err)
}
return nil
}
func BootstrapStarterLayout(ctx context.Context) error {
ctx, cancelFn := context.WithTimeout(ctx, 2*time.Second)
defer cancelFn()
client, err := wstore.DBGetSingleton[*waveobj.Client](ctx)
if err != nil {
log.Printf("unable to find client: %v\n", err)
return fmt.Errorf("unable to find client: %w", err)
}
if len(client.WindowIds) < 1 {
return fmt.Errorf("error bootstrapping layout, no windows exist")
}
windowId := client.WindowIds[0]
window, err := wstore.DBMustGet[*waveobj.Window](ctx, windowId)
if err != nil {
return fmt.Errorf("error getting window: %w", err)
}
workspace, err := wstore.DBMustGet[*waveobj.Workspace](ctx, window.WorkspaceId)
if err != nil {
return fmt.Errorf("error getting workspace: %w", err)
}
tabId := workspace.ActiveTabId
starterLayout := GetStarterLayout()
err = ApplyPortableLayout(ctx, tabId, starterLayout, false)View on GitHub (pinned to a4447c1563)
Solutions
- Create a window (and its workspace/tab) before calling BootstrapStarterLayout
- Ensure normal app startup (which opens a window) completes before AgreeTos triggers bootstrap
- If testing, seed the client with a valid WindowIds entry or skip bootstrap in headless mode
Example fix
// before
err := wcore.BootstrapStarterLayout(ctx) // client has no windows
// after
if len(client.WindowIds) == 0 {
// create a window first, or skip
return nil
}
err := wcore.BootstrapStarterLayout(ctx) Defensive patterns
Strategy: validation
Validate before calling
client, err := wstore.DBGetSingleton[*waveobj.Client](ctx)
if err != nil { return err }
if len(client.WindowIds) < 1 {
// create a window or skip bootstrap
return nil
} Type guard
func hasWindows(c *waveobj.Client) bool { return c != nil && len(c.WindowIds) > 0 } Try / catch
if err := wcore.BootstrapStarterLayout(ctx); err != nil {
if strings.Contains(err.Error(), "no windows exist") {
// skip starter layout; normal startup will create a window
}
} Prevention
- Only call BootstrapStarterLayout after a window exists
- In headless/test environments, skip bootstrap instead of calling it blindly
- Recreate the client record if a reset wiped its WindowIds
When it happens
Trigger: AgreeTos -> BootstrapStarterLayout while the client singleton has zero windows: no window has ever been created, or all windows were deleted from the store.
Common situations: Calling bootstrap too early in startup before the first window is created; a wiped/reset wave store where windows were removed but the client record persists; headless/CI environments where no window was opened.
Related errors
- unable to find client: %w
- error getting window: %w
- error applying starter layout: %w
- opacity must be between 0.0 and 1.0
- setting background: %v
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/878cca8324f58aa1.
Report an issue: GitHub.