[vector_graphics] useHtmlRenderObject() doesn't catch engine surface-creation failures; SVG widgets crash repeatedly (CanvasKitError on 3.38.x, bare JS string on 3.44.x)
- Dominant language
- Dart
- Stars
- 179k
- Forks
- 31.1k
- PR merge metrics
- PR metrics pending
Description
### What package does this bug report belong to?
vector_graphics
### What target platforms are you seeing this bug on?
Web
### Have you already upgraded your packages?
Yes
### Dependency versions
pubspec.lock
```lock
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.7.0"
async:
dependency: transitive
description:
name: async
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
url: "https://pub.dev"
source: hosted
version: "2.13.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
characters:
dependency: transitive
description:
name: characters
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev"
source: hosted
version: "1.4.1"
clock:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
source: hosted
version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
source: hosted
version: "1.19.1"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
url: "https://pub.dev"
source: hosted
version: "1.0.9"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_svg:
dependency: "direct main"
description:
name: flutter_svg
sha256: "35882981abcbfb8c15b286f0cd690ff25bac12d95eff3e25ee207f37d4c42e7f"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: transitive
description:
name: http
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
url: "https://pub.dev"
source: hosted
version: "1.6.0"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
lints:
dependency: transitive
description:
name: lints
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
url: "https://pub.dev"
source: hosted
version: "6.1.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
version: "0.13.0"
meta:
dependency: transitive
description:
name: meta
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
url: "https://pub.dev"
source: hosted
version: "1.18.0"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
path_parsing:
dependency: transitive
description:
name: path_parsing
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
url: "https://pub.dev"
source: hosted
version: "7.0.2"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_span:
dependency: transitive
description:
name: source_span
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
url: "https://pub.dev"
source: hosted
version: "1.10.2"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
source: hosted
version: "1.12.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.dev"
source: hosted
version: "1.4.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.dev"
source: hosted
version: "1.2.2"
test_api:
dependency: transitive
description:
name: test_api
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
url: "https://pub.dev"
source: hosted
version: "0.7.11"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vector_graphics:
dependency: transitive
description:
name: vector_graphics
sha256: "2306c03da2ba81724afeb589c351ebbc0aa7d86005925be8f8735856dbe5e42d"
url: "https://pub.dev"
source: hosted
version: "1.2.2"
vector_graphics_codec:
dependency: transitive
description:
name: vector_graphics_codec
sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146"
url: "https://pub.dev"
source: hosted
version: "1.1.13"
vector_graphics_compiler:
dependency: transitive
description:
name: vector_graphics_compiler
sha256: "142a9146f447d15b10bdc00e21d5f4d83e5b32bb5f8f8f5a04c75311344923a3"
url: "https://pub.dev"
source: hosted
version: "1.2.6"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
source: hosted
version: "2.2.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
url: "https://pub.dev"
source: hosted
version: "15.2.0"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
xml:
dependency: transitive
description:
name: xml
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
url: "https://pub.dev"
source: hosted
version: "6.6.1"
sdks:
dart: ">=3.10.4 <4.0.0"
flutter: ">=3.35.0"
```
### Steps to reproduce
Minimal reproducible example: https://github.com/aqrc/flutter_web_canvaskit_svg_repro (pinned to Flutter 3.44.6 via FVM; also reproduces on 3.38.5)
1. `fvm flutter pub get`
2. `fvm flutter run -d chrome` (any browser/mode works)
3. On the Splash screen tap **Open Intro**. The tap arms the simulated WebGL failure, then navigation builds the first `SvgPicture`, which crashes its subtree. The rest of the app keeps rendering.
**Note on the trigger:** in production this occurs organically on Safari/WebKit when WebGL surface creation starts failing mid-session — an environmental condition that can't be reproduced on demand. The MRE's `web/index.html` recreates that engine state deterministically (verified against the engine source of both 3.38.5 and 3.44.6): the script is dormant until the first pointerdown so the app boots and renders normally; once armed, `MakeOnScreenGLSurface` returns null for new surfaces (already-created surfaces keep working), and the engine's software fallback fails with the verbatim string CanvasKit's `MakeSWCanvasSurface` throws on production Safari. From `Picture.toImageSync` onward, everything is the real engine and the real package code.
Production telemetry since 2026-04-20: **~1,130 events, ~110 users**, essentially all Safari/WebKit (Mobile Safari 16.6–26.5, Mobile Safari WKWebView, desktop Safari; a single Firefox event out of >1,000). Flutter 3.38.5 web app (CanvasKit renderer, dart2js release), embedded in an iframe on a partner page; the app had rendered and navigated several routes before each crash, so only the engine's picture-rasterization surface failed, not the display surface. The failure persists within a session: the affected screen auto-advances a story carousel every 5 s, rebuilding the SVG; each rebuild re-runs the probe and crashes again. 1,042 events span 187 traces, with single sessions recording up to 84 occurrences — i.e. ~7 minutes of sustained WebGL failure.
### Expected results
Same behavior as the `StateError` / `NoSuchMethodError` cases fixed in 1.2.1 ([flutter/packages#11685](https://github.com/flutter/packages/pull/11685)): the probe treats the failure as "raster path unavailable" and falls back to the HTML compatibility render object.
### Actual results
The value thrown by `toImageSync` escapes `useHtmlRenderObject()` and crashes the `VectorGraphic` build. Because `_cachedUseHtmlRenderObject` is only cached on success, every subsequent SVG build repeats the crash. What escapes is engine-version-dependent:
- **Flutter 3.38.x**: `Surface._makeSoftwareCanvasSurface` wraps the failure into `CanvasKitError('Failed to create CPU-based surface: ...')`.
- **Flutter 3.44.x**: the refactored engine (`_recreateSoftwareSkSurface`) has no such wrapper, so CanvasKit's **bare JS string** propagates as-is — it is not even a Dart `Error`.
Production stack (3.38.5, dart2js release, vector_graphics 1.1.19; the vector_graphics code is unchanged on 1.2.2 / main):
```
CanvasKitError: Failed to create CPU-based surface: Canvas with id [object HTMLCanvasElement] was not found.
org-dartlang-sdk:///lib/_engine/engine/canvaskit/surface.dart:474 Surface._makeSoftwareCanvasSurface
org-dartlang-sdk:///lib/_engine/engine/canvaskit/surface.dart:278 Surface.createOrUpdateSurface
org-dartlang-sdk:///lib/_engine/engine/canvaskit/picture.dart:94 CkPicture.toImageSync
vector_graphics-1.1.19/lib/src/render_object_selection.dart:28 useHtmlRenderObject
vector_graphics-1.1.19/lib/src/vector_graphics.dart:464 _VectorGraphicWidgetState.build
```
The inner error comes from CanvasKit's `MakeSWCanvasSurface`: its `instanceof HTMLCanvasElement` check fails for the engine's canvas element (which nevertheless stringifies as `[object HTMLCanvasElement]`), so it falls through to `document.getElementById(...)` and throws. So the engine's own CPU fallback is broken in this state — arguably a separate engine-side bug — but for this probe the HTML render object is the intended degradation path regardless.
### Analysis / suggested fix
`useHtmlRenderObject()` currently catches `UnsupportedError`, `StateError`, and `NoSuchMethodError`. Enumerating engine failure types has proven to be a losing game: `UnsupportedError` (old HTML renderer), `StateError` / `NoSuchMethodError` (iOS 18.7 Safari, fixed in 1.2.1), `CanvasKitError` (engine ≤3.38 — declared in `dart:_engine`, unimportable by package code), and now a bare JS string (engine ≥3.44 — unmatchable by any `on` type except `Object`).
The probe exists to answer one question — "can `toImageSync` be used?" — and any throw answers "no". Suggest replacing the type-specific clauses with a catch-all:
```dart
try {
image = picture.toImageSync(1, 1);
_cachedUseHtmlRenderObject = false;
} on Object catch (_) {
// If toImageSync fails for any reason (the failure type varies by engine
// version and renderer), the raster path is unusable — fall back to the
// HTML compatibility render object.
_cachedUseHtmlRenderObject = true;
} finally {
...
}
```
### Code sample
Full runnable example in the MRE repo https://github.com/aqrc/flutter_web_canvaskit_svg_repro.
The app side is just `SvgPicture.asset(...)`; the simulation script is in `web/index.html`.
### Screenshots or Videos
Screenshots / Video demonstration
### Logs
Flutter 3.44.6 / vector_graphics 1.2.2 (debug, Chrome) — bare JS string escapes
```
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following message was thrown building VectorGraphic(dirty, dependencies: [Directionality,
_LocalizationsScope-[GlobalKey#31715]], state: _VectorGraphicWidgetState#febb2):
Canvas with id [object OffscreenCanvas] was not found
The relevant error-causing widget was:
VectorGraphic
VectorGraphic:file:///.../vector_graphics-1.2.2/lib/src/vector_graphics.dart:76:24
════════════════════════════════════════════════════════════════════════════════════════════════════
Another exception was thrown: Canvas with id [object OffscreenCanvas] was not found
Another exception was thrown: Canvas with id [object OffscreenCanvas] was not found
```
Flutter 3.38.5 / vector_graphics 1.2.2 (debug, Chrome) — CanvasKitError escapes
```
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following CanvasKitError was thrown building VectorGraphic(dirty, dependencies: [Directionality,
_LocalizationsScope-[GlobalKey#ddb88]], state: _VectorGraphicWidgetState#8d3d3):
Failed to create CPU-based surface: Canvas with id [object OffscreenCanvas] was not found.
When the exception was thrown, this was the stack:
lib/_engine/engine/canvaskit/surface.dart 474:7 [_makeSoftwareCanvasSurface]
lib/_engine/engine/canvaskit/surface.dart 448:16 [_createNewSurface]
lib/_engine/engine/canvaskit/surface.dart 278:23 createOrUpdateSurface
lib/_engine/engine/canvaskit/picture.dart 94:40 toImageSync
package:vector_graphics/src/render_object_selection.dart 28:20 useHtmlRenderObject
package:vector_graphics/src/vector_graphics.dart 458:40 get _webRenderObject
package:vector_graphics/src/vector_graphics.dart 487:11 build
════════════════════════════════════════════════════════════════════════════════════════════════════
Another exception was thrown: CanvasKitError: Failed to create CPU-based surface: Canvas with id [object OffscreenCanvas] was not found.
```
(In the MRE in Chrome the engine uses an `OffscreenCanvas`, hence `[object OffscreenCanvas]`; on production Safari the same path reports `[object HTMLCanvasElement]`.)
### Flutter Doctor output
Doctor output
```console
[✓] Flutter (Channel stable, 3.44.6, on macOS 26.5 25F71 darwin-arm64, locale en-IL)
[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0)
[✓] Xcode - develop for iOS and macOS (Xcode 26.5)
[✓] Chrome - develop for the web
[✓] Connected device (2 available)
[✓] Network resources
• No issues found!
```
Contributor guide
Assessment
This issue has not been assessed yet.