Description
We are seeing sporadic native crashes in the picture rendering path. There
are two distinct signatures, both on the main thread inside
RNSkPictureRenderer::performDraw, and both consistent with the renderer
touching an SkPicture whose refcount has already dropped to zero (i.e. the
JS side replaced/released the picture while a redraw was in flight):
- SIGTRAP —
sk_abort_no_print fired from SkRefCntBase::ref() while
performDraw copies its sk_sp<SkPicture> (resurrecting a dead object).
- SIGSEGV / EXC_BAD_ACCESS —
SkCanvas::drawPicture dereferencing a
garbage pointer (one report flags "possible pointer authentication
failure", consistent with a freed object).
We have collected 8 crash reports since 2026-07-01 with these signatures.
The crashes are sporadic and include occurrences while the app is idle on a
screen with a continuously animating canvas.
Environment
@shopify/react-native-skia: 2.6.4 (also inspected 2.6.5–2.6.9
changelogs/commits — no picture-lifecycle changes found in that range)
- React Native: 0.86.0, New Architecture enabled (Fabric), Hermes
- iOS — reports below are from the iOS Simulator (Xcode 26.3 / 17C529,
macOS 26.1 25B78, arm64); the app is also exercised on physical devices
- Rendering: default Metal canvas provider (
RNSkMetalCanvasProvider)
- react-native-reanimated: not installed (JS-driven rendering only)
Stack signature 1 — SIGTRAP, refcount assert on a dead SkPicture
AayuPlus-2026-07-01-213531.ips (EXC_BREAKPOINT / SIGTRAP, main thread):
0 sk_abort_no_print()
1 SkRefCntBase::ref() const::'lambda'()::operator()() const
2 SkRefCntBase::ref() const
3 SkPicture* SkSafeRef<SkPicture>(SkPicture*)
4 sk_sp<SkPicture>::sk_sp(sk_sp<SkPicture> const&)
5 sk_sp<SkPicture>::sk_sp(sk_sp<SkPicture> const&)
6 RNSkia::RNSkPictureRenderer::performDraw(std::shared_ptr<RNSkia::RNSkCanvasProvider>)
7 RNSkia::RNSkPictureRenderer::renderImmediate(std::shared_ptr<RNSkia::RNSkCanvasProvider>)
8 RNSkia::RNSkView::requestRedraw()::'lambda'()::operator()() const
… (std::function plumbing)
Reading of this stack: requestRedraw → renderImmediate → performDraw
copies the current picture sk_sp — and the copy constructor's SkSafeRef
hits Skia's "ref on 0-refcount object" abort, i.e. the picture was already
destroyed when the redraw ran.
Stack signature 2 — SIGSEGV inside drawPicture
AayuPlus-2026-07-08-121149.ips (EXC_BAD_ACCESS / SIGSEGV,
KERN_INVALID_ADDRESS at 0x0000beadd8b87e08 — possible pointer authentication failure, main thread):
0 SkCanvas::drawPicture(SkPicture const*, SkMatrix const*, SkPaint const*)
1 SkCanvas::drawPicture(SkPicture const*)
2 SkCanvas::drawPicture(sk_sp<SkPicture> const&)
3 RNSkia::RNSkPictureRenderer::performDraw(...)::'lambda'(SkCanvas*)::operator()(SkCanvas*) const
… (std::function plumbing)
11 RNSkMetalCanvasProvider::renderToCanvas(std::function<void (SkCanvas*)> const&)
12 RNSkia::RNSkPictureRenderer::performDraw(std::shared_ptr<RNSkia::RNSkCanvasProvider>)
13 RNSkia::RNSkPictureRenderer::renderImmediate(std::shared_ptr<RNSkia::RNSkCanvasProvider>)
Same entry path, but here the dead picture survives the copy and crashes
inside drawPicture when its contents are dereferenced.
Reproduction context
We don't have a minimal reproducer yet (it's a sporadic race), but the app
context is:
- Multiple
<Canvas> components on screen, each rendering an imperative
SkPicture: createPicture(cb) is called every frame from a
requestAnimationFrame → setState loop (~60fps), and the result is
rendered through a single <Picture picture={p} /> element per canvas.
Each frame therefore replaces the previous picture, which becomes
garbage-collectable immediately.
- Crashes occur sporadically, including while the app idles on the screen
with the animating canvases (nothing else happening).
- Frequency increased when we added a canvas with higher picture churn
(more draws per picture, still one picture/frame) — but the SIGTRAP
signature predates that change on the same 2.6.4, when our only Skia
usage was declarative JSX canvases re-rendered via requestAnimationFrame
- state (so the picture in question was the renderer's internal one).
- 8 reports between 2026-07-01 and 2026-07-08 across normal dev usage.
Mitigations we applied (reduce frequency — may help locate the window)
- Two-frame picture retention on the JS side: keeping the last two
SkPicture objects referenced in a ref so GC cannot finalize a picture
that a not-yet-completed redraw may still read. This points at the
window being between the JS-side release/GC of the previous picture and
performDraw consuming it.
- Pausing the rAF loop when the screen is not focused (less churn,
fewer chances to race).
Both reduce, not eliminate, the exposure — the race looks like it needs a
fix at the RNSkPictureRenderer level (e.g. taking a strong ref under the
same lock/thread that swaps the picture, or deferring picture destruction
to the render thread).
Possibly related open issues
Crash reports
The two full (redacted) .ips crash reports exceed the issue body size
limit, so they are attached as a gist:
https://gist.github.com/iamdiwakar/69bbcef50c7ceb0f62f78493b4c76d6f
AayuPlus-2026-07-01-213531.ips — SIGTRAP signature
AayuPlus-2026-07-08-121149.ips — SIGSEGV signature
Redactions are limited to incident_id/crashReporterKey/modelCode;
all thread stacks, binary images (incl. UUIDs), and exception details
are intact. Happy to test candidate patches.
Description
We are seeing sporadic native crashes in the picture rendering path. There
are two distinct signatures, both on the main thread inside
RNSkPictureRenderer::performDraw, and both consistent with the renderertouching an
SkPicturewhose refcount has already dropped to zero (i.e. theJS side replaced/released the picture while a redraw was in flight):
sk_abort_no_printfired fromSkRefCntBase::ref()whileperformDrawcopies itssk_sp<SkPicture>(resurrecting a dead object).SkCanvas::drawPicturedereferencing agarbage pointer (one report flags "possible pointer authentication
failure", consistent with a freed object).
We have collected 8 crash reports since 2026-07-01 with these signatures.
The crashes are sporadic and include occurrences while the app is idle on a
screen with a continuously animating canvas.
Environment
@shopify/react-native-skia: 2.6.4 (also inspected 2.6.5–2.6.9changelogs/commits — no picture-lifecycle changes found in that range)
macOS 26.1 25B78, arm64); the app is also exercised on physical devices
RNSkMetalCanvasProvider)Stack signature 1 — SIGTRAP, refcount assert on a dead SkPicture
AayuPlus-2026-07-01-213531.ips(EXC_BREAKPOINT / SIGTRAP, main thread):Reading of this stack:
requestRedraw→renderImmediate→performDrawcopies the current picture
sk_sp— and the copy constructor'sSkSafeRefhits Skia's "ref on 0-refcount object" abort, i.e. the picture was already
destroyed when the redraw ran.
Stack signature 2 — SIGSEGV inside drawPicture
AayuPlus-2026-07-08-121149.ips(EXC_BAD_ACCESS / SIGSEGV,KERN_INVALID_ADDRESS at 0x0000beadd8b87e08 — possible pointer authentication failure, main thread):Same entry path, but here the dead picture survives the copy and crashes
inside
drawPicturewhen its contents are dereferenced.Reproduction context
We don't have a minimal reproducer yet (it's a sporadic race), but the app
context is:
<Canvas>components on screen, each rendering an imperativeSkPicture:createPicture(cb)is called every frame from arequestAnimationFrame→setStateloop (~60fps), and the result isrendered through a single
<Picture picture={p} />element per canvas.Each frame therefore replaces the previous picture, which becomes
garbage-collectable immediately.
with the animating canvases (nothing else happening).
(more draws per picture, still one picture/frame) — but the SIGTRAP
signature predates that change on the same 2.6.4, when our only Skia
usage was declarative JSX canvases re-rendered via
requestAnimationFrameMitigations we applied (reduce frequency — may help locate the window)
SkPictureobjects referenced in a ref so GC cannot finalize a picturethat a not-yet-completed redraw may still read. This points at the
window being between the JS-side release/GC of the previous picture and
performDrawconsuming it.fewer chances to race).
Both reduce, not eliminate, the exposure — the race looks like it needs a
fix at the
RNSkPictureRendererlevel (e.g. taking a strong ref under thesame lock/thread that swaps the picture, or deferring picture destruction
to the render thread).
Possibly related open issues
teardown"
Crash reports
The two full (redacted)
.ipscrash reports exceed the issue body sizelimit, so they are attached as a gist:
https://gist.github.com/iamdiwakar/69bbcef50c7ceb0f62f78493b4c76d6f
AayuPlus-2026-07-01-213531.ips— SIGTRAP signatureAayuPlus-2026-07-08-121149.ips— SIGSEGV signatureRedactions are limited to
incident_id/crashReporterKey/modelCode;all thread stacks, binary images (incl. UUIDs), and exception details
are intact. Happy to test candidate patches.