GopeedLab/gopeed · error · WebViewRpcException

EVALUATION_FAILED

EVALUATION_FAILED

Error message

e.toString()

What it means

Thrown by WebViewRpcPageSession.execute (webview_rpc_service.dart:461) when InAppWebViewController.evaluateJavascript throws while injecting the wrapper script — a native/channel-level failure, not a JavaScript error in your expression (those flow back through the callback channel and surface as error 38). The pending completer is removed and the exception string is wrapped as EVALUATION_FAILED.

Source

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

  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,
          args: args,
        ),
      );
    } catch (e) {
      _pendingExecutions.remove(requestId);
      throw WebViewRpcException(
        code: 'EVALUATION_FAILED',
        message: e.toString(),
      );
    }

    try {
      final result = await completer.future.timeout(
        const Duration(seconds: 30),
        onTimeout: () {
          _pendingExecutions.remove(requestId);
          throw WebViewRpcException(
            code: 'TIMEOUT',
            message: 'javascript execution timeout',
          );
        },
      );
      return result;
    } finally {
      _pendingExecutions.remove(requestId);

View on GitHub (pinned to 7b7327ffb3)

Solutions

  1. Await page.open's returned pageId and give the first goto/execute a small readiness check before heavy use
  2. Catch EVALUATION_FAILED whose message is a PlatformException, reopen the page (page.open + goto), and re-run the expression once
  3. Serialize close/execute per page in the client so dispose cannot race an in-flight execute
  4. Read e.toString() in the message to identify the platform cause before retrying

Example fix

// before
page.execute('window.__poll()', []); // unawaited, races page.close()

// after
try {
  await page.execute('window.__poll()', []);
} on RpcError catch (e) {
  if (e.code != 'EVALUATION_FAILED') rethrow;
  await reopenAndNavigate(); // page.open + page.goto, then retry once
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return await page.execute(expression, args);
} on RpcError catch (e) {
  if (e.code == 'EVALUATION_FAILED' && !e.message.contains('Error:')) {
    // native PlatformException, not a JS error: reopen page and retry once
    await reopenPage();
    return await page.execute(expression, args);
  }
  rethrow;
}

Prevention

When it happens

Trigger: Calling 'page.execute' while the webview is being torn down (dispose races the call); executing on a page whose native controller died; platform channel errors on Android/iOS when the WebView is detached. Contrast: a syntax error in 'expression' posts back via the bridge and raises error 38 instead.

Common situations: Fire-and-forget execute calls racing page.close or app backgrounding on mobile (Android destroys the webview); memory pressure reclaiming the headless webview; executing immediately after page.open before the controller fully attaches.

Related errors


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