After integrating Sentry error monitoring, Release Adoption stayed at 0%, and Source Maps never resolved.
Two days of debugging, six pitfalls cleared.
Background
The project is a Next.js 15 static export deployed on Cloudflare Pages. I wanted Sentry error monitoring so user errors show readable stack traces.
Seems simple: Install SDK → Upload Source Maps → Done.
Reality:
0% Adoption → SDK not initialized → Switch to CDN
Source Map upload fails → sentry-cli crashes → Switch to HTTP API
Wrong region → Token parse failure → Write parser logic
Release mismatch → Filename wrong → Unicode escape
Pitfall 1: Release Adoption Stays at 0%
Symptoms
Sentry Dashboard:
- Releases list shows the release ✅
- But Adoption is 0% ❌
- Events list is empty ❌
Root Cause
@sentry/browser got tree-shaken away by SWC.
// ❌ This code is eliminated by SWC during build
import * as Sentry from '@sentry/browser'
Sentry.init({ dsn: '...', release: process.env.NEXT_PUBLIC_SENTRY_RELEASE })
@sentry/browser uses heavy lazy imports and conditional exports internally. SWC's minification pass treats these as dead code and removes them entirely.
Solution: Use Sentry CDN
// app/layout.tsx
export default function RootLayout({ children }) {
return (
<html>
<head>
<script
src="https://browser.sentry-cdn.com/7.113.0/bundle.min.js"
crossorigin="anonymous"
/>
<script dangerouslySetInnerHTML={{ __html: `
Sentry.init({
dsn: '${process.env.NEXT_PUBLIC_SENTRY_DSN}',
release: '${process.env.NEXT_PUBLIC_SENTRY_RELEASE}',
environment: 'production'
})
`}} />
</head>
<body>{children}</body>
</html>
)
}
The CDN script lives outside SWC's processing pipeline and won't be tree-shaken.
Result: Release Adoption goes from 0% to >0% ✅
Pitfall 2: sentry-cli Crashes on GitHub Actions
Symptoms
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npx @sentry/cli sourcemaps upload out/
Runtime error:
Error: The module '/node_modules/@sentry/cli/bin/sentry-cli'
was compiled against a different Node.js version
Root Cause
sentry-cli is a Rust-compiled binary with Node.js FFI bindings that are incompatible with the Actions Node.js version.
Solution: Direct HTTP API
// scripts/upload-sourcemaps.js
const fs = require('fs')
const https = require('https')
// Parse org ID and region from Auth Token
function parseSentryToken(authToken) {
const payload = authToken.replace('sntrys_', '')
const decoded = JSON.parse(Buffer.from(payload, 'base64').toString())
return {
orgId: decoded.org_slug,
region: decoded.host === 'https://sentry.io' ? 'us' : 'eu'
}
}
// multipart/form-data upload
function uploadSourceMap(options) {
const boundary = '----FormBoundary' + Math.random().toString(36).slice(2)
// ... build multipart body and send via https.request()
}
Pitfall 3: Wrong Region
Symptoms
404 Not Found: /api/0/organizations/myorg/releases/
Root Cause
Sentry has two regions: us.sentry.io and eu.sentry.io. My org is in US, but the default request went to EU.
Solution
The Auth Token encodes region info:
// Sentry token: sntrys_ prefix + base64(JSON)
const token = 'sntrys_eyJyZWdpb25FbmRwb2ludCI6ICJodHRwczovL3VzLnNlbnRyeS5pbyJ9'
const payload = Buffer.from(token.slice(7), 'base64').toString()
const info = JSON.parse(payload)
// info.regionEndpoint → "https://us.sentry.io"
Pitfall 4: Release Name Mismatch
Symptoms
Source Maps upload succeeds, but Dashboard still shows "Missing."
Root Cause
The release name in Source Maps doesn't match what the SDK reports:
SDK reports: "release": "utlkit-20260521-abc1234"
Source Map: "release": "utlkit-abc1234" ← missing date
Solution
Generate the release name from one script, used by both SDK and upload:
// scripts/generate-sentry-release.js
const { execSync } = require('child_process')
const commitSha = execSync('git rev-parse --short HEAD').toString().trim()
const date = new Date().toISOString().split('T')[0]
const release = `utlkit-${date}-${commitSha}`
console.log(release)
Pitfall 5: Unicode Escape Corrupts Filenames
Symptoms
Source Maps uploaded but Sentry shows garbled paths.
Root Cause
JSON.stringify converts non-ASCII characters to \uXXXX, which Sentry's API can't parse correctly.
Final Workflow
name: Upload Sentry Source Maps
on:
push:
branches: [main]
jobs:
upload:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- run: npm ci
- run: npm run build
- run: node scripts/generate-sentry-release.js > .sentry-release
- run: node scripts/upload-sourcemaps.js
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
Summary
| Pitfall | Symptom | Solution |
|---|---|---|
| SWC tree-shakes SDK | Adoption 0% | Use Sentry CDN |
| sentry-cli Node incompatibility | Binary crash | Direct HTTP API |
| Wrong region | 404 | Parse region from Token |
| Release mismatch | Source Map Missing | Unified release name |
| Unicode escape | Garbled filenames | Handle encoding properly |
| Dynamic import optimized away | SDK not initialized | CDN script |
Core lesson: Sentry's official docs assume Node.js SSR or bundler plugins. The static export + CDN SDK combination requires custom upload logic.
Project
utlkit.com — All formatter tools run purely in the browser. The solutions above are running stably in production.
Top comments (0)