Epure
SDK

Verify Sentry source maps on a self-hosted host (no Symbolicator)

Upload JS/TS .map files with DSN auth, match the SDK release string, demangle stacks in-process. 2 MB cap. No Symbolicator.

On this pageShow
  1. Still minified after upload
  2. Match the release string
  3. DSN-auth .map upload
  4. 2 MB body cap
  5. JS/TS only
  6. Verify demangle checklist
  7. Limitations
  8. Questions

On Epure, upload JS/TS .map files under the same release string your official Sentry SDK sends. Demangle runs in-process on the Rust binary with DSN auth and a 2 MB body cap. There is no Symbolicator beside Epure. Still-minified stacks usually mean a missing map or a release-string mismatch.

You already run official @sentry/browser or @sentry/node and want readable stacks on a two-container self-host (Rust binary + PostgreSQL 16). Full Sentry self-host with Symbolicator workers is the undesired alternative for this path. This how-to is the verify path: pin release, DSN-upload .map, throw from the minified bundle, confirm original frames in Issues.

Unique wedge: Epure demangles JS/TS maps in-process on the Rust binary. Symbolicator is not an Epure component. Bias disclosed: Epure first-party docs are the source of truth for Epure behavior; Sentry Symbolicator docs describe a different stack.

Primary how-to: Source maps. Acceptance: Verify. Pin/init: Quickstart. Host shape: Installation. Sibling context: Sentry SDK compatible, Two-container error tracking, Epure vs Sentry.

Still minified after upload

If stacks stay minified after a successful map upload, check that SDK release equals upload {version}, that the Releases UI lists the artifacts, that upload used DSN auth rather than a session cookie, and that the map body is <= 2 MB. Missing maps still ingest and group; frames simply stay minified. Do not assume Symbolicator workers are required on Epure.

Still-minified stacks: symptom to fix (Source maps / Verify)

Stacks still minified

Fix (Source maps / Verify)
`Sentry.init({ release })` must equal `{version}` in the upload URL; check Releases UI for artifacts; DSN auth (not cookie); body <= 2 MB

Session cookie on upload

Fix (Source maps / Verify)
Rejected; use DSN `X-Sentry-Auth`

Trying dSYM / ProGuard / NDK

Fix (Source maps / Verify)
Out of scope; JS/TS `.map` only

202 but Issues empty

Fix (Source maps / Verify)
Wrong project, env filter, or worker flush; wait ~1s; filter `local`; `docker compose logs epure`

401 invalid_dsn

Fix (Source maps / Verify)
Copy key from Settings -> SDK connection

403 dsn_revoked

Fix (Source maps / Verify)
Create or rotate key

403 project_mismatch

Fix (Source maps / Verify)
URL project id must match DSN

Assuming Symbolicator required

Fix (Source maps / Verify)
Wrong for Epure; demangle is in-process on the Rust binary
DimensionFix (Source maps / Verify)
Stacks still minified`Sentry.init({ release })` must equal `{version}` in the upload URL; check Releases UI for artifacts; DSN auth (not cookie); body <= 2 MB
Session cookie on uploadRejected; use DSN `X-Sentry-Auth`
Trying dSYM / ProGuard / NDKOut of scope; JS/TS `.map` only
202 but Issues emptyWrong project, env filter, or worker flush; wait ~1s; filter `local`; `docker compose logs epure`
401 invalid_dsnCopy key from Settings -> SDK connection
403 dsn_revokedCreate or rotate key
403 project_mismatchURL project id must match DSN
Assuming Symbolicator requiredWrong for Epure; demangle is in-process on the Rust binary

Match the release string

Tag the SDK with the same release string you use in the upload URL {version} (docs example: my-app@1.0.0). If Sentry.init({ release }) and the upload version differ, demangle is skipped for that event and stacks can stay minified. Build with source maps enabled (Vite, webpack, esbuild, or equivalent), then upload each .map for that release.

When showing install lines, pin the Quickstart versions:

npm install @sentry/browser@7.120.0 or npm install @sentry/node@7.120.0

Example init shape (release string must match the upload {version}): import * as Sentry from "@sentry/browser"; Sentry.init({ dsn: "http://YOUR_PUBLIC_KEY@localhost:8080/YOUR_PROJECT_ID", release: "my-app@1.0.0" });

Upload flow checklist (Source maps):

  • Build with source maps enabled.
  • Tag the SDK with the same string you upload under (release: "my-app@1.0.0" in the docs example).
  • Upload each .map via the release files endpoint.
  • Ship. New events for that release demangle when a matching map is present.
  • Missing maps still ingest and group; frames stay minified.

{version} must not contain .., /, or \.

DSN-auth .map upload

Upload maps with a multipart POST to /api/{project_id}/releases/{version}/files/ using DSN X-Sentry-Auth (sentry_version=7, public key, secret). Session cookies are rejected on this endpoint. A successful upload returns 201 Created with JSON shaped like { "id", "name" }.

Request shape (Source maps): POST /api/{project_id}/releases/{version}/files/ with Content-Type: multipart/form-data and X-Sentry-Auth: Sentry sentry_version=7, sentry_key={public_key}, sentry_secret={secret_key}.

Multipart fields for release files upload (Source maps)

file

Required
yes
Notes (Source maps)
Artifact bytes (typically `*.map`)

name

Required
no
Notes (Source maps)
Artifact path; paths like `~/dist/app.min.js.map` accepted
DimensionRequiredNotes (Source maps)
fileyesArtifact bytes (typically `*.map`)
namenoArtifact path; paths like `~/dist/app.min.js.map` accepted

Live curl from Source maps:

curl -sS -D - -o /tmp/epure-map.json -X POST "http://localhost:8080/api/${PROJECT_ID}/releases/my-app@1.0.0/files/" -H "X-Sentry-Auth: Sentry sentry_version=7, sentry_key=${PUBLIC}, sentry_secret=${SECRET}" -F "name=~/dist/app.min.js.map" -F "file=@./dist/app.min.js.map;type=application/json"

Expected (Source maps): HTTP/1.1 201 Created and JSON shaped like { "id": "660e8400-e29b-41d4-a716-446655440001", "name": "~/dist/app.min.js.map" }.

2 MB body cap

Map upload bodies must be <= 2 MB. Stay under the documented Source maps limit. Do not invent a status code for oversize bodies if you only have those two primary docs open; prefer the wording "body <= 2 MB".

This 2 MB cap is a hard limit on the release files upload path. Keep maps under it when you build and upload.

JS/TS only

Symbolication on Epure is JS/TS .map only: no dSYM, ProGuard, or NDK. Browser and Node .map files are in scope; other languages keep raw frames.

Symbolication scope on Epure (Source maps)

Browser / Node JS/TS .map

Epure demangle
In scope (in-process on Rust binary)

dSYM (iOS)

Epure demangle
Not supported

ProGuard (Android)

Epure demangle
Not supported

NDK

Epure demangle
Not supported

Non-JS/TS runtimes

Epure demangle
Raw frames only
DimensionEpure demangle
Browser / Node JS/TS .mapIn scope (in-process on Rust binary)
dSYM (iOS)Not supported
ProGuard (Android)Not supported
NDKNot supported
Non-JS/TS runtimesRaw frames only

Verify demangle checklist

After upload, capture an error from the minified bundle with the matching release, then open the event in Issues. Event detail should show original file, function, and line, not only app.min.js:1:.... This is the optional JS/TS sourcemaps confirm step on the Verify docs; baseline ingest still expects 202 Accepted plus an unresolved issue for the exception type.

Optional sourcemaps confirm (Verify):

  • Upload minified assets + .map for a release (Source maps).
  • Capture an error from the minified bundle with matching release.
  • Event detail should show original file / function / line, not only app.min.js:1:....
  • Other languages: raw frames only.

Baseline verify throw (Verify): Sentry.captureException(new Error("Epure verify test"));

Store curl (Verify panel; seed defaults) if you need a raw ingest check before the map path:

curl -sS -D - -o /tmp/epure-ingest.json -X POST "http://localhost:8080/api/550e8400-e29b-41d4-a716-446655440000/store/" -H "X-Sentry-Auth: Sentry sentry_version=7, sentry_key=a1b2c3d4e5f6g7h8i9j0, sentry_secret=supersecretdevkey" -H "Content-Type: application/json" -d '{"platform":"javascript","exception":{"values":[{"type":"Error","value":"Epure try-it"}]}}'

Expected ingest (Verify success table): 202 Accepted + { "id": "<uuid>" }, then an unresolved issue for that exception type. The sourcemaps verify path ends when Issues shows demangled frames for the matching release.

Host reminder: documented Compose is two containers (epure + postgres:16-alpine). See Installation and Quickstart.

Limitations

Epure demangles minified JS/TS stack frames in-process on the Rust binary at ingest when a matching .map is present for that release. You do not run Symbolicator or remote symbol workers next to Epure. Epure self-host for this path is two Compose services (Rust binary + PostgreSQL 16); Redis and Kafka are not defaults.

Negative scope (must-read):

  • Epure does not ship or require Symbolicator or remote symbol workers.
  • Symbolication is JS/TS .map only: no dSYM, ProGuard, or NDK.
  • Epure is not 100% Sentry compatible.
  • Session replay, APM tracing, and profiling are not Epure features.
  • Map bodies over 2 MB are out of limits.
  • Redis, Kafka, and ClickHouse are not default Epure services for this path.
  • This article does not claim demangle latency or percent-of-frames-recovered figures.

Use this guide if

  • You already point official Sentry SDKs at a 2-container Epure host and need readable JS/TS stacks.
  • You want the no-Symbolicator verify path: matching release -> DSN multipart upload -> Issues demangle check.
  • You accept a 2 MB map body cap and JS/TS-only symbolication.

Read something else if

Deep links: Source maps | Verify | Quickstart | Installation.

FAQ

Questions

Does Epure need Symbolicator for JavaScript source maps?
No. Epure demangles JS/TS frames in-process on the Rust binary. Symbolicator is not an Epure component and is not required beside your Compose stack.
How do I upload a source map to self-hosted Epure?
Send a multipart POST to `/api/{project_id}/releases/{version}/files/` with DSN `X-Sentry-Auth`, a `file` field for the `.map`, and an optional `name` such as `~/dist/app.min.js.map`. Expect `201 Created` with an `id` and `name` when the upload succeeds.
Why are my stacks still minified after upload?
Usually the SDK `release` does not equal the upload `{version}`, the artifact is missing in Releases, the upload used a session cookie instead of DSN auth, or the body exceeded 2 MB. Fix the match and auth first; missing maps still accept events with minified frames.
Can Epure symbolicate iOS or Android crashes?
No. Symbolication on Epure is JS/TS `.map` only. dSYM, ProGuard, and NDK paths are not supported; non-JS/TS runtimes show raw frames.
Is Epure 100% Sentry compatible for source maps?
No. Epure is not 100% Sentry compatible. It accepts official Sentry SDK DSN traffic for a JS/TS `.map` upload and in-process demangle subset, with a 2 MB body cap and no Symbolicator parity.
Do session cookies work for map upload?
No. Map upload authenticates with the project DSN only. Session cookies on the release files endpoint are rejected.
What SDK versions should I pin in examples?
When showing browser or Node install lines, pin `@sentry/browser@7.120.0` or `@sentry/node@7.120.0` (Quickstart). Always set the same `release` string you use in the upload URL.