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
- Await page.open's returned pageId and give the first goto/execute a small readiness check before heavy use
- Catch EVALUATION_FAILED whose message is a PlatformException, reopen the page (page.open + goto), and re-run the expression once
- Serialize close/execute per page in the client so dispose cannot race an in-flight execute
- 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
- Serialize close/execute per page so dispose cannot race an in-flight execute
- Distinguish native failures (message is a PlatformException string) from JS errors (message is a browser error)
- Reopen-and-retry once is usually enough; persistent failure means the webview environment is broken
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
- NAVIGATION_FAILED
- TIMEOUT
- execute expects a string or function, got %T
- execute expects a string or function, got %T (%q)
- missing or invalid "%s"
AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16).
Data as JSON: /api/errors/4fc58d897d3dc673.
Report an issue: GitHub.