On this page

Crash reporting

Crashes and errors from your web, Node.js, Flutter, Android and iOS apps, grouped into issues, with the stack, breadcrumbs and the API call that failed right before the crash.

LimitFreeProScaleEnterprise
Crash reports per project5,000 per month100,000 per month1,000,000 per month10,000,000 per month
Crash events kept7 days30 days90 days90 days
crash_sourcemaps_mb50 MB1.0 GB5.0 GB10 GB

Crash reporting collects the uncaught exceptions of your apps, and the errors you report yourself, in your project. Reports of the same bug are grouped into one issue, so you see how many events and users each bug has, in which releases it happens and whether a fix worked. Because the SDK already makes your app’s API calls, every report shows which request to your project failed right before the crash, and Studio lists the failed requests from the project’s logs around it.

#Set it up

  1. Open Studio › Crashes and click Turn on crash reporting.
  2. Call crashes.start() once when your app starts, with the version of the app as the release. Nothing is captured or sent before start(), so if you ask your users for consent, call it after they agreed.
import { createClient } from 'stitchdb-js'
const stitchdb = createClient(STITCHDB_URL, STITCHDB_ANON_KEY)

stitchdb.crashes.start({
  release: '1.4.0',               // your app's version
  environment: 'production',
  beforeSend: (report) => report, // change a report, or return null to drop it
})

#Report errors yourself

Errors you catch are reported as handled. Add context (it is shown with the event) and, if the automatic grouping does not fit, your own fingerprint: reports with the same fingerprint are one issue.

JavaScript
try {
  await checkout(cart)
} catch (e) {
  stitchdb.crashes.captureException(e, { context: { cart: cart.id }, fingerprint: ['checkout'] })
  showError()
}

stitchdb.crashes.captureMessage('Payment sheet closed without a result')
stitchdb.crashes.addBreadcrumb({ type: 'ui', message: 'tap Checkout' })
await stitchdb.crashes.flush() // send now, such as before a serverless function returns

#What a report has

PartWhat it is
ExceptionThe error type, the message and the stack. Stacks from JavaScript engines, Dart, Java and Kotlin and Apple symbol lines are read into frames; your own code is told apart from libraries.
BreadcrumbsThe last 50 things that happened before: every API call the SDK made (method, path, the names of the query parameters but not their values, status, error code, duration), page changes on the web and anything you add.
The API call before the crashThe last failed API call in the two minutes before the crash, else the last call. A crash a few seconds after a 403 with 42501 usually means a policy refused a row the app expected.
Release, environment, deviceWhat you passed to start(), and the OS, version, model and locale the SDK knows.
UserThe signed-in user, from their token. An app cannot report as another user; only the service key may name one.

#Issues

Without your own fingerprint, a report is grouped by the error type and the three topmost frames of your own code (function and file, without line numbers and without build hashes in file names), so the same bug in the next build is the same issue. Without a stack it is grouped by the type and the message, with numbers, ids and e-mail addresses taken out.

  • Resolve an issue once you shipped the fix. If it happens again in a release that is new since then, it opens again as a regression and alerts; events from the old release do not.
  • Ignore an issue to stop its alerts. It is still counted.
  • Crash-free users per release: the share of installs that opened the app that day and did not crash. The SDK sends one small session a day per install and release for this.

#Alerts

A new issue and a regression alert by e-mail to the project owner, to the project’s webhooks (events crash.issue.new and crash.issue.regressed) and, if you add a bot, to a Telegram chat. Only crashes alert by default; turn on alerts for handled errors in the settings. At most 20 alerts per project an hour; Send a test alert checks the setup.

#Privacy

Before a report is kept, values of fields named like passwords, tokens, secrets, keys, cookies and authorization are replaced with [scrubbed], e-mail addresses and JWTs in messages and URLs are removed, and so are the values of query parameters such as token or api_key. Add your own field names under More fields to scrub. Reports are stored in your project’s database (schema _stitch_crashes), not in a third-party service.

#Read issues from your server

With the service key, for a dashboard of your own or a CI check after a release:

bash
curl 'https://<ref>.p.stitchdb.com/crashes/v1/issues?status=open&release=1.4.0' \
  -H "apikey: $SERVICE_KEY" -H "Authorization: Bearer $SERVICE_KEY"
# also: GET /crashes/v1/issues/<id>, PATCH /crashes/v1/issues/<id> {"status":"resolved"}, GET /crashes/v1/releases

#Source maps

Minified JavaScript stacks point to app.3f9c2a1b.js:1:48213. Upload the source maps of each release and reports of that release show the original file, line, column and function, with a few lines of your source around each frame of your own code (when the map has sourcesContent). Stacks are read when a report arrives, before it is grouped, so issues group by your original code. The maps stay in your project; the box your project runs on reads them, and they are never sent anywhere else.

bash
# the CLI 2.2 or newer
npm install -g https://stitchdb.com/cli/stitchdb.tgz
# after your build, in CI or by hand (the release is the one you pass to crashes.start)
stitchdb sourcemaps upload dist --release 1.4.0
stitchdb sourcemaps list --release 1.4.0
stitchdb sourcemaps delete --release 1.3.0

Each map is stored under the path of its minified file: dist/assets/app.3f9c2a1b.js.map becomes /assets/app.3f9c2a1b.js. Use --url-prefix when the folder is served from a sub-path. A frame on another host or under a deeper path still finds its map; if two maps have the same file name in different folders, give the exact path. Turn on source maps in your bundler (build.sourcemap: true, devtool: 'source-map' or similar); you do not need to publish the .map files on your site.

Without the CLI, send the map with the service key:

bash
curl -X POST 'https://<ref>.p.stitchdb.com/crashes/v1/sourcemaps?release=1.4.0&name=/assets/app.3f9c2a1b.js' \
  -H "apikey: $SERVICE_KEY" -H "Authorization: Bearer $SERVICE_KEY" \
  --data-binary @dist/assets/app.3f9c2a1b.js.map
# gzip is fine too: -H "Content-Encoding: gzip" --data-binary @app.js.map.gz
# also: GET /crashes/v1/sourcemaps?release=1.4.0, DELETE /crashes/v1/sourcemaps?release=1.4.0[&name=/assets/app.3f9c2a1b.js]
  • Upload the maps before the release goes out. Reports that arrived earlier are shown with original frames once the map is uploaded, but they stay in the issue they were grouped into.
  • One map can be up to 64 MB. All maps of a project together count toward the plan’s source map storage (the unpacked size). They are stored compressed in your project’s database and stay until you delete them: delete the maps of releases nobody uses any more.
  • Index maps (with sections) work; Android R8 mapping files, iOS dSYMs and native crashes are not supported yet.

#Limits

Each report counts toward the plan’s reports per month. After that, new reports still count on their issue but are not kept until the next month. Up to 20 events per issue a day are kept with all their details, for the plan’s days; issues and their counts stay. Environments you list in the settings are the only ones kept.