fix: attach componentStack as string so ErrorBoundary works on React Native#21
Merged
fix: attach componentStack as string so ErrorBoundary works on React Native#21
Conversation
…Native ErrorBoundary was wrapping the componentStack in `new Blob([...])` before handing it to the bugsplat SDK's FormData body. React Native's FormData polyfill can't serialize browser Blob objects, so the multipart body never assembles and fetch throws `TypeError: Network request failed` on every render error caught on iOS and Android. Passing the componentStack as a plain string works in all environments because `FormData.append(name, string)` is universally supported. Requires bugsplat@>=9.1 to accept string in `BugSplatAttachment.data`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates ErrorBoundary’s component stack attachment format to be React Native compatible by sending the componentStack as a plain string (instead of a browser Blob), avoiding RN FormData serialization failures during crash posting.
Changes:
- Update
createComponentStackAttachmentto setdatato the rawcomponentStackstring. - Expand the inline doc comment to explain the React Native
FormData/Blobincompatibility. - Add a unit test asserting the attachment
datais a string containing the component name.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/ErrorBoundary.tsx |
Switch component stack attachment payload from Blob to string and document the RN rationale. |
spec/ErrorBoundary.spec.tsx |
Add test coverage to ensure ErrorBoundary posts a string attachment payload for the component stack. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Browser FormData needs a real Blob to emit a multipart file part (with
a filename in Content-Disposition). React Native's FormData can't
serialize browser Blobs, but it can stream a file from a `data:` URI
when given RN's `{ uri, type }` shape.
Build the right shape once here based on navigator.product, so the
underlying bugsplat SDK never has to know which runtime it's in.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 17, 2026
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
daveplunkett
approved these changes
Apr 17, 2026
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9.1 adds the BugSplatFileRef shape to the BugSplatAttachment.data
union, which this branch relies on when building the componentStack
attachment for React Native (data URI inside {uri, type}). Without
the bump, CI's npm ci pulled 9.0.x and tsc failed with
`'uri' does not exist in type 'Blob | Uint8Array'`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ErrorBoundary's componentStack attachment was built with a runtime isReactNative check that picked between `new Blob` on web and a data-URI file-ref on RN. Platform detection doesn't belong in a cross-platform React helper, so this extends Scope (the existing DI container ErrorBoundary already reads from) with a pluggable function slot `createComponentStackAttachment`. - Scope stores the builder plus a web-friendly default that wraps the stack in a text/plain Blob. - ErrorBoundary reads the builder from scope via the existing optional `getCreateComponentStackAttachment()` getter; unknown/bare duck-typed scopes fall through to the default. - appScope is now exported so runtime-specific wrappers (notably @bugsplat/expo) can call setCreateComponentStackAttachment() in their own init() without constructing and threading a separate Scope. - Skip the attachment entirely if React hands us a null componentStack. No behavior change for existing web consumers — the default builder produces the same Blob they were getting before. RN consumers install their own builder via the new seam. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Scope's constructor always installs a default builder, so every
`new Scope()` exposes a valid `getCreateComponentStackAttachment()`.
The optional chain + fallback in ErrorBoundary only existed to support
bare duck-typed scope objects in tests — a narrow case not worth the
branch.
Tighten the scope prop to `Pick<Scope, 'getClient' | 'getCreateComponentStackAttachment'>`
and call the getter directly. Tests switch from ad-hoc `{ getClient }`
objects to real `new Scope(client)` instances, which is a closer
simulation of production usage anyway. ErrorBoundary's default
`scope` prop now points at the exported `appScope` directly instead
of wrapping `getBugSplat` in a one-off object literal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 9 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
CI and CD were bumped to Node 24 in 60ce396 but .nvmrc was left at 18, which causes "works locally, fails in CI" drift for anyone who uses `nvm use` on this repo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
TypeError: Network request failedwhen<ErrorBoundary>catches a render error in a React Native app (@bugsplat/expo, bare RN). React Native's FormData polyfill can't serialize browserBlobobjects, so the old `new Blob([componentStack])` attachment caused the multipart body to never assemble and `fetch` rejected before the request left the device.bugsplatSDK stays uniform — it just serializes the `BugSplatAttachment` it receives. This is the only library that has to care about its runtime, so the single `isReactNative` check lives here.Dependency
Requires bugsplat-js#78 for the `BugSplatFileRef` shape in the `BugSplatAttachment.data` union. I'll bump `"bugsplat": "^9.1.0"` after that SDK publishes.
Test plan
🤖 Generated with Claude Code