Skip to content

Commit d61e0aa

Browse files
fix: Block MDX compilation at Vercel runtime to protect release registry (#15953)
## Summary This PR blocks MDX compilation from running at Vercel runtime, which was causing: 1. **Registry overload**: Every runtime MDX compilation fetches from `release-registry.services.sentry.io`, causing traffic spikes that crashed the registry 2. **esbuild errors**: Runtime compilation fails with "read-only file system" errors (DOCS-915, 4.9M+ events) ## Root Cause Analysis When users hit URLs that don't exist (404s) or certain edge cases in Next.js routing, the page component tries to render before falling back to not-found. This triggers: 1. `getFileBySlugWithCache()` is called for a non-existent path 2. Cache check is skipped (only runs when `CI` env var is set) 3. MDX compilation is attempted via `bundleMDX()` 4. `remarkVariables` plugin fetches from release registry 5. esbuild tries to write to `/var/task/public/mdx-images` (read-only on Lambda) 6. Error is thrown, registry has been hammered ## The Fix Add a guard at the start of `getFileBySlug()` that throws immediately if we're at Vercel runtime: ```typescript const isVercelRuntime = process.env.VERCEL && !process.env.CI && process.env.NODE_ENV !== 'development'; if (isVercelRuntime) { throw new Error(`[MDX Runtime Error] Attempted to compile MDX at Vercel runtime for slug "${slug}"...`); } ``` ## Why This Is Safe - All 9229 docs pages are statically generated during CI builds (`force-static` + `generateStaticParams`) - MDX compilation at runtime was **never intended** to work - the build dependencies are excluded from the Lambda bundle - This just makes the failure explicit and fast, protecting the registry - Local development and CI builds are unaffected ## Fixes - DOCS-915 (4.9M events) - DOCS-9H5, DOCS-9N0, DOCS-9HB (related ENOENT errors) --------- Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
1 parent 623aaa9 commit d61e0aa

2 files changed

Lines changed: 58 additions & 1 deletion

File tree

src/components/include.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ export async function Include({name}: Props) {
2323
try {
2424
doc = await getFileBySlugWithCache(`includes/${name}`);
2525
} catch (e) {
26-
if (e.code === 'ENOENT') {
26+
// Handle file not found (ENOENT) and runtime MDX compilation attempts gracefully
27+
if (e.code === 'ENOENT' || e.code === 'MDX_RUNTIME_ERROR') {
2728
return null;
2829
}
2930
throw e;

src/mdx.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,22 @@ const isSupported = (
174174
let getDocsFrontMatterCache: Promise<FrontMatter[]> | undefined;
175175

176176
export function getDocsFrontMatter(): Promise<FrontMatter[]> {
177+
// Block filesystem scanning at Vercel runtime.
178+
// Frontmatter should only be scanned during CI builds - the doc tree is pre-computed.
179+
// See: DOCS-83Q, DOCS-9A5, DOCS-9RE
180+
const isVercelRuntime =
181+
process.env.VERCEL && !process.env.CI && process.env.NODE_ENV !== 'development';
182+
183+
if (isVercelRuntime) {
184+
return Promise.reject(
185+
new Error(
186+
`[MDX Runtime Error] Attempted to scan docs frontmatter at Vercel runtime. ` +
187+
`This should not happen - the doc tree should be pre-computed during CI. ` +
188+
`If you're seeing this error, the requested path may not exist or was not included in generateStaticParams().`
189+
)
190+
);
191+
}
192+
177193
if (!getDocsFrontMatterCache) {
178194
getDocsFrontMatterCache = getDocsFrontMatterUncached();
179195
}
@@ -269,6 +285,22 @@ export async function getDevDocsFrontMatterUncached(): Promise<FrontMatter[]> {
269285
let getDevDocsFrontMatterCache: Promise<FrontMatter[]> | undefined;
270286

271287
export function getDevDocsFrontMatter(): Promise<FrontMatter[]> {
288+
// Block filesystem scanning at Vercel runtime.
289+
// Frontmatter should only be scanned during CI builds - the doc tree is pre-computed.
290+
// See: DOCS-83Q, DOCS-9A5, DOCS-9RE
291+
const isVercelRuntime =
292+
process.env.VERCEL && !process.env.CI && process.env.NODE_ENV !== 'development';
293+
294+
if (isVercelRuntime) {
295+
return Promise.reject(
296+
new Error(
297+
`[MDX Runtime Error] Attempted to scan develop-docs frontmatter at Vercel runtime. ` +
298+
`This should not happen - the doc tree should be pre-computed during CI. ` +
299+
`If you're seeing this error, the requested path may not exist or was not included in generateStaticParams().`
300+
)
301+
);
302+
}
303+
272304
if (!getDevDocsFrontMatterCache) {
273305
getDevDocsFrontMatterCache = getDevDocsFrontMatterUncached();
274306
}
@@ -487,6 +519,30 @@ export const addVersionToFilePath = (filePath: string, version: string) => {
487519
};
488520

489521
export async function getFileBySlug(slug: string): Promise<SlugFile> {
522+
// Block MDX compilation at Vercel runtime.
523+
// MDX should only be compiled during CI builds - all pages are statically generated.
524+
// If this code path is hit at runtime, it means:
525+
// 1. A 404 page is being accessed (Next.js tries to render before showing not-found)
526+
// 2. Some edge case in routing is bypassing static generation
527+
//
528+
// Without this check, runtime MDX compilation would:
529+
// - Fetch from release-registry.services.sentry.io (hammering the registry)
530+
// - Fail with "read-only file system" errors from esbuild
531+
//
532+
// See: DOCS-915, DOCS-9H5, DOCS-9N0, DOCS-9HB
533+
const isVercelRuntime =
534+
process.env.VERCEL && !process.env.CI && process.env.NODE_ENV !== 'development';
535+
536+
if (isVercelRuntime) {
537+
const error = new Error(
538+
`[MDX Runtime Error] Attempted to compile MDX at Vercel runtime for slug "${slug}". ` +
539+
`This should not happen - all pages should be pre-built during CI. ` +
540+
`If you're seeing this error, the requested path may not exist or was not included in generateStaticParams().`
541+
) as Error & {code: string};
542+
error.code = 'MDX_RUNTIME_ERROR';
543+
throw error;
544+
}
545+
490546
// no versioning on a config file
491547
const configPath = path.join(root, slug.split(VERSION_INDICATOR)[0], 'config.yml');
492548

0 commit comments

Comments
 (0)