bugwatch docs

Source maps & symbolication

For agents: uploading is a binary flow with no MCP tool — tell the human to run sentry-cli as shown below with a release:write token. You can verify the result: list_releases(org="acme") shows artifactBundles per release, and get_fix_context frames carry file/line/context once maps resolve (event.symbolication.partial with reasons flags frames that did not).

Minified JavaScript frames are useless until the processor can find the matching source map. Bugwatch resolves maps in two ways, tried in order:

  1. Debug ID — the debugId injected into the bundle and its map by modern build plugins (withSentryConfig, @sentry/vite-plugin, sentry-cli sourcemaps inject). Exact and release-independent.
  2. Release + dist + URL path — for maps uploaded without debug IDs, the frame's release, the artifact dist, and the script URL path (/assets/app.js → /assets/app.js.map) look up the bundle uploaded under that release.

Set release in the SDK to the same string you upload under, or only debug-ID resolution can work.

The API implements the sentry-cli chunk-upload protocol, so existing CI steps work by pointing --url at Bugwatch:

export SENTRY_AUTH_TOKEN=bwt_…   # needs release:write
sentry-cli --url https://api.bugwatch.io sourcemaps inject ./dist
sentry-cli --url https://api.bugwatch.io sourcemaps upload \
  --org acme --project web --release 1.4.2 ./dist

Framework plugins take the same URL as sentryUrl / url (Next.js withSentryConfig, Nuxt sourceMapsUploadOptions, the Vite/webpack plugins). Native symbol uploads for mobile (debug-files upload) use the same token and URL.

What happens underneath, for debugging:

Step Route Notes
Options GET /api/0/organizations/{org}/chunk-upload/ Advertises 8 MiB chunks, SHA-1 names, gzip, up to 64 chunks (32 MiB) per request, accept: ["artifact_bundles"] only
Chunks POST /api/0/organizations/{org}/chunk-upload/ multipart parts named by the SHA-1 of the raw chunk; checksums verified
Assemble POST /api/0/organizations/{org}/artifactbundle/assemble/ {checksum, chunks[], projects[], version?, dist?} → {state: "ok" | "not_found" | "error", missingChunks[]}; assembled synchronously; the release is created if missing

Legacy release-file uploads are not advertised; only artifact bundles (a zip with manifest.json + files/) are accepted.

Native endpoint (no CLI)

POST /v1/orgs/{org}/projects/{project}/artifact-bundles with release:write:

{
  "release": "1.4.2",
  "dist": "web",
  "files": [
    { "name": "~/assets/app.js.map", "debugId": "3f2a1c4e-…", "content": "{\"version\":3,…}" }
  ]
}
  • Up to 500 files, 25 MB per file, 200 MB per bundle (JSON string content).
  • release, dist, and debugId are optional, but the release must already exist (create_release or POST /v1/orgs/{org}/releases), otherwise 404.
  • Names are normalised to a /-rooted path: ~/assets/x.js.map, https://cdn/assets/x.js.map, and assets/x.js.map all become /assets/x.js.map.

Both paths store files in R2 under artifacts/{project}/{sha256}, index them in the control database, and warm the lookup manifests the processor reads (artifacts:{project}:{debugId} and relmap:{project}:{release}:{dist}).

Verifying

  • list_releases → artifactBundles should be ≥ 1 for the release.
  • Send a test error from the built bundle and open the issue: in-app frames should show file, line, and three lines of context on each side.
  • The processed event lists any frames it could not resolve under symbolication_failures (surfaced as event.symbolication in fix context). Common causes: release mismatch between SDK and upload, maps uploaded without .map suffix mapping, or a dist set on upload but not in the SDK.

Beyond JavaScript

Mobile symbol files (dSYM, ProGuard/R8, Flutter split-debug-info) go through the same sentry-cli URL and token; see Mobile & desktop. Server-side languages report symbolic frames and need nothing.