diff --git a/apps/studio/instrumentation-client.ts b/apps/studio/instrumentation-client.ts index f5acf9591e6..e47a2fb1b02 100644 --- a/apps/studio/instrumentation-client.ts +++ b/apps/studio/instrumentation-client.ts @@ -98,6 +98,26 @@ Sentry.init({ // Enable performance monitoring tracesSampleRate: 1.0, // Capture 100% of transactions for performance monitoring + integrations: (() => { + // On staging (Vercel preview deployments), SENTRY_AUTH_TOKEN is not available so the + // webpack plugin doesn't annotate chunks with the applicationKey. Without annotations, + // thirdPartyErrorFilterIntegration considers every frame third-party and drops all events. + // Skip the integration on staging and rely solely on allowUrls for filtering there. + if (process.env.NEXT_PUBLIC_ENVIRONMENT === 'staging') return [] + + const thirdPartyErrorFilterIntegration = (Sentry as any).thirdPartyErrorFilterIntegration + if (!thirdPartyErrorFilterIntegration) return [] + + // Drop errors whose stack trace only contains third-party frames (browser extensions, + // injected scripts, etc.). This uses build-time code annotation via the applicationKey + // in next.config.js to reliably distinguish our code from third-party code. + return [ + thirdPartyErrorFilterIntegration({ + filterKeys: ['supabase-studio'], + behaviour: 'drop-error-if-exclusively-contains-third-party-frames', + }), + ] + })(), // Only capture errors originating from our own code. // This is a whitelist on the source URL in stack frames — it drops errors from @@ -106,8 +126,9 @@ Sentry.init({ /https?:\/\/(.*\.)?supabase\.(com|co|green|io)/, /app:\/\//, // Next.js rewrites source URLs to app:// with source maps // On Vercel preview deployments, assets are served from *.vercel.app (no CDN). - // Allow those so errors from preview branches reach Sentry. - ...(process.env.NEXT_PUBLIC_VERCEL_ENV !== 'production' + // Only allow this in staging — never in production — to avoid widening the + // allowlist for real users. + ...(process.env.NEXT_PUBLIC_ENVIRONMENT === 'staging' ? [/https?:\/\/.*\.vercel\.app/] : []), ], diff --git a/apps/studio/next.config.js b/apps/studio/next.config.js index 0b9ea369e84..a2fe4b4ab67 100644 --- a/apps/studio/next.config.js +++ b/apps/studio/next.config.js @@ -33,6 +33,14 @@ const nextConfig = { basePath: process.env.NEXT_PUBLIC_BASE_PATH, assetPrefix: getAssetPrefix(), output: 'standalone', + + // Generate client-side source maps in production so the Sentry webpack plugin can pair + // each JS chunk with its .map file, inject matching debug IDs into both, and upload them. + // Without this, Next.js uses hidden-source-map mode (no //# sourceMappingURL= in JS files), + // the plugin can't find the source maps, and debug IDs only end up in JS files — not in + // the uploaded source maps — causing "No source file with matching Debug ID" in Sentry. + // The .map files are deleted before CDN upload by Sentry's deleteSourcemapsAfterUpload. + productionBrowserSourceMaps: true, async rewrites() { return [ { @@ -635,6 +643,16 @@ module.exports = deleteSourcemapsAfterUpload: true, }, + // Match the CDN URL structure so Sentry can correlate uploaded source maps to + // incoming stack frames. Assets are served from: + // {SUPABASE_ASSETS_URL}/{SITE_NAME}/{COMMIT_SHA_12}/_next/static/... + // Without this, Sentry indexes artifacts as ~/_next/... and can't find them + // when frames arrive with the full CDN path. + urlPrefix: (() => { + const assetPrefix = getAssetPrefix() + return assetPrefix ? `${assetPrefix}/_next` : '~/_next' + })(), + // Automatically tree-shake Sentry logger statements to reduce bundle size disableLogger: true,