GopeedLab/gopeed · error · WebViewRpcException

TIMEOUT

TIMEOUT

Error message

navigation timeout after ${timeoutMs}ms

What it means

Thrown by WebViewRpcPageSession.goto (webview_rpc_service.dart:436) when the navigation future (load event or DOM-ready, per waitUntil) does not settle within the caller-supplied timeoutMs. On timeout the service clears _navigation and throws WebViewRpcException(code: 'TIMEOUT'). The page may still finish loading afterwards — the timeout only abandons the wait.

Source

Thrown at ui/flutter/lib/app/rpc/webview_rpc_service.dart:436

      );
    }
    final future = switch (waitStrategy) {
      'domcontentloaded' => Future.any<void>([
          _waitForDomReady(),
          navigation.future,
        ]),
      _ => navigation.future,
    };
    if (timeoutMs == null || timeoutMs <= 0) {
      await future;
      return;
    }
    await future.timeout(
      Duration(milliseconds: timeoutMs),
      onTimeout: () {
        _navigation = null;
        throw WebViewRpcException(
          code: 'TIMEOUT',
          message: 'navigation timeout after ${timeoutMs}ms',
        );
      },
    );
  }

  Future<dynamic> execute(String expression, List<dynamic> args) async {
    final controller = await _controllerOrThrow();
    final requestId = 'exec-$pageId-${++_executeSeq}';
    final completer = Completer<dynamic>();
    _pendingExecutions[requestId] = completer;

    try {
      await controller.evaluateJavascript(
        source: buildWebViewExecuteScript(
          channelName: callbackChannelName,
          requestId: requestId,
          expression: expression,

View on GitHub (pinned to 7b7327ffb3)

Solutions

  1. Switch waitUntil to "domcontentloaded", which fires much earlier than "load"
  2. Increase timeoutMs (e.g. 30000) for heavy pages or slow networks
  3. Treat TIMEOUT as non-fatal: verify readiness afterwards with page.execute of document.readyState
  4. Check connectivity before blaming the page; retry the goto once network is confirmed

Example fix

// before
await page.goto(url, timeoutMs: 5000); // 'load' default, heavy page

// after
await page.goto(url, timeoutMs: 30000, waitUntil: 'domcontentloaded');
Defensive patterns

Strategy: retry

Validate before calling

if (timeoutMs !== undefined && (typeof timeoutMs !== 'number' || timeoutMs <= 0)) {
  throw new TypeError('pass a positive timeoutMs or omit it');
}

Try / catch

try {
  await page.goto(url, timeoutMs: 30000, waitUntil: 'domcontentloaded');
} on RpcError catch (e) {
  if (e.code == 'TIMEOUT') {
    final state = await page.execute('document.readyState', []); // page may be usable already
    if (state != 'complete') rethrow;
  } else {
    rethrow;
  }
}

Prevention

When it happens

Trigger: page.goto with timeoutMs: 5000 against a slow site where 'load' (all subresources) exceeds 5s; a page with hanging analytics/tracking requests; offline or throttled network; waitUntil 'load' on a page that never fires window.onload because a script blocks.

Common situations: CI runners with slow egress; mobile networks; sites with long-polling or huge assets; timeouts tuned on desktop then reused on mobile; pages that intentionally never finish loading (chat apps).

Understand the failure class

Related errors


AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16). Data as JSON: /api/errors/e4af6ccbe5f4df25. Report an issue: GitHub.