wavetermdev/waveterm · warning

no webcontents found with blockid ${data.blockid}

Error message

no webcontents found with blockid ${data.blockid}

What it means

WslConn.Close waits for any in-flight waiter (a registered goroutine performing connection work) to finish before closing. It polls conn.HasWaiter every 10ms and, if the waiter is still registered after 2 seconds, returns this timeout error instead of closing while work is in progress. Reaching this error means the close was refused, not that the connection failed.

Source

Thrown at emain/emain-wsh.ts:28

import { getWebContentsByBlockId, webGetSelector } from "./emain-web";
import { createBrowserWindow, getWaveWindowById, getWaveWindowByWorkspaceId } from "./emain-window";

export class ElectronWshClientType extends WshClient {
    constructor() {
        super("electron");
    }

    async handle_webselector(rh: RpcResponseHelper, data: CommandWebSelectorData): Promise<string[]> {
        if (!data.tabid || !data.blockid || !data.workspaceid) {
            throw new Error("tabid and blockid are required");
        }
        const ww = getWaveWindowByWorkspaceId(data.workspaceid);
        if (ww == null) {
            throw new Error(`no window found with workspace ${data.workspaceid}`);
        }
        const wc = await getWebContentsByBlockId(ww, data.tabid, data.blockid);
        if (wc == null) {
            throw new Error(`no webcontents found with blockid ${data.blockid}`);
        }
        const rtn = await webGetSelector(wc, data.selector, data.opts);
        return rtn;
    }

    async handle_notify(rh: RpcResponseHelper, notificationOptions: WaveNotificationOptions) {
        new Notification({
            title: notificationOptions.title,
            body: notificationOptions.body,
            silent: notificationOptions.silent,
        }).show();
    }

    async handle_getupdatechannel(rh: RpcResponseHelper): Promise<string> {
        return getResolvedUpdateChannel();
    }

    async handle_focuswindow(rh: RpcResponseHelper, windowId: string) {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Wait a moment and retry the Close/Reconnect — the in-flight waiter usually completes
  2. Investigate what is holding HasWaiter (connserver startup, version check) and why it exceeds 2s
  3. If the waiter is permanently stuck, restart the Wave connection/block to clear state
  4. Retry with backoff rather than immediately reconnecting after a status change

Example fix

// before
err := conn.Reconnect(ctx) // can hit waiter timeout during handshake
// after
if err := conn.Reconnect(ctx); err != nil && strings.Contains(err.Error(), "timeout waiting for waiter") {
    time.Sleep(3 * time.Second)
    err = conn.Reconnect(ctx) // retry after in-flight work settles
}
Defensive patterns

Strategy: retry

Validate before calling

if conn.HasWaiter.Load() {
    time.Sleep(3 * time.Second) // let in-flight work finish before closing
}

Try / catch

err := conn.Reconnect(ctx)
if err != nil && strings.Contains(err.Error(), "timeout waiting for waiter") {
    time.Sleep(3 * time.Second)
    err = conn.Reconnect(ctx) // retry with backoff
}

Prevention

When it happens

Trigger: Reconnect (or direct Close) is called while another operation holds the waiter flag for over 2 seconds — e.g. a long-running connserver startup or handshake still in flight.

Common situations: Reconnect attempted immediately after a network drop while the connection was still handshaking; slow WSL startup exceeding the 2s window; a waiter goroutine stuck/hung.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/e6e3c9253bae83f9. Report an issue: GitHub.