Fix OG image generation font loading error
og image not loading correctly. like https://forums.basehub.com/api/og/post?owner=basehub-ai&repo=forums&postNumber=68 this gives me an error
Error: ENOENT: no such file or directory, open '/var/task/apps/web/node_modules/geist/dist/fonts/geist-mono/GeistMono-Regular.ttf' at async j (.next/server/chunks/[root-of-the-server]__e50c36d6..js:1:4607) at async d (.next/server/chunks/[root-of-the-server]__e50c36d6..js:1:8814) at async l (.next/server/chunks/[root-of-the-server]__e50c36d6..js:1:9855) at async Module.U (.next/server/chunks/[root-of-the-server]__e50c36d6..js:1:10933) { errno: -2, code: 'ENOENT', syscall: 'open', path: '/var/task/apps/web/node_modules/geist/dist/fonts/geist-mono/GeistMono-Regular.ttf' }
Fix and verify the build works before pushing
PR created: https://github.com/basehub-ai/forums/pull/139
##Summary
Problem: OG image routes (post, home, repo, user) were failing with ENOENT on Vercel because they loaded Geist Mono fonts using:
readFile(join(process.cwd(), "node_modules/geist/dist/fonts/geist-mono/GeistMono-Regular.ttf"))
In a monorepo deployed on Vercel, process.cwd() resolves to apps/web/ but dependencies are hoisted to the workspace root node_modules/, so the path apps/web/node_modules/geist/... doesn't exist.
Fix: Created a shared apps/web/app/api/og/fonts.ts utility that uses createRequire(import.meta.url).resolve("geist/font/mono") to dynamically find where the geist package is actually installed, then resolves font file paths relative to that. This works regardless of whether dependencies are hoisted or not.
All 4 affected OG routes (post, home, repo, user) now import from this shared utility instead of duplicating the broken process.cwd() path logic.
Build err:
19:11:24.510 Running build in Washington, D.C., USA (East) – iad1
19:11:24.510 Build machine configuration: 4 cores, 8 GB
19:11:24.621 Cloning github.com/basehub-ai/forums (Branch: forums/fix-og-font-loading-7a418, Commit: 418fcf5)
19:11:24.883 Cloning completed: 261.000ms
19:11:28.555 Restored build cache from previous deployment (BSjgyUCeh9dQ81KQXeSEMG625GWA)
19:11:28.814 Running "vercel build"
19:11:29.669 Vercel CLI 50.11.0
19:11:29.768 > Installing dependencies before config compilation...
19:11:29.774 Installing dependencies...
19:11:29.815 bun install v1.3.6 (d530ed99)
19:11:30.982
19:11:30.983 Checked 1285 installs across 1412 packages (no changes) [1189.00ms]
19:11:31.096 > Detected Turbo. Adjusting default settings...
19:11:31.329 Running "install" command: bun install...
19:11:31.334 bun install v1.3.6 (d530ed99)
19:11:31.907
19:11:31.907 Checked 1285 installs across 1412 packages (no changes) [574.00ms]
19:11:31.916 Detected Next.js version: 16.1.4
19:11:31.916 Running "cd ../.. && turbo run build --filter={apps/web}..."
19:11:32.147
19:11:32.147 Attention:
19:11:32.147 Turborepo now collects completely anonymous telemetry regarding usage.
19:11:32.147 This information is used to shape the Turborepo roadmap and prioritize features.
19:11:32.147 You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
19:11:32.147 https://turborepo.dev/docs/telemetry
19:11:32.147
19:11:32.267 • Packages in scope: forums
19:11:32.267 • Running build in 1 packages
19:11:32.267 • Remote caching enabled
19:11:32.335 forums:build: cache miss, executing 0f4f4cac8428633b
19:11:32.339 forums:build: $ VERCEL=0 xmcp build && next build && ([ "$VERCEL_ENV" = "production" ] && bun run publish:cli || true)
19:11:32.602 forums:build: XMCP Building for production...
19:11:33.179 forums:build: ✔ Built Adapter
19:11:33.241 forums:build: ✔ Compiled in 432ms
19:11:38.490 forums:build: Discovering workflow directives 3628ms
19:11:39.176 forums:build: Created steps bundle 684ms
19:11:39.212 forums:build: Created intermediate workflow bundle 35ms
19:11:39.214 forums:build: Creating webhook route
19:11:39.215 forums:build: Creating manifest...
19:11:39.221 forums:build: Created manifest with 8 step(s) and 1 workflow(s) 6ms
19:11:39.247 forums:build: ▲ Next.js 16.1.4 (Turbopack, Cache Components)
19:11:39.247 forums:build:
19:11:39.294 forums:build: Creating an optimized production build ...
19:12:11.060 forums:build: ✓ Compiled successfully in 31.2s
19:12:11.062 forums:build: Running TypeScript ...
19:12:22.948 forums:build: Collecting page data using 3 workers ...
19:12:24.139 forums:build: TypeError: The "path" argument must be of type string. Received type number (654304)
19:12:24.140 forums:build: at o (app/api/og/fonts.ts:20:15)
19:12:24.140 forums:build: at module evaluation (app/api/og/fonts.ts:24:3)
19:12:24.140 forums:build: at module evaluation (app/api/og/post/route.tsx:4:1)
19:12:24.140 forums:build: 18 | // geistMonoEntry resolves to e.g. .../node_modules/geist/dist/mono.js
19:12:24.140 forums:build: 19 | // fonts are at .../node_modules/geist/dist/fonts/geist-mono/
19:12:24.141 forums:build: > 20 | return join(dirname(geistMonoEntry), "fonts", "geist-mono", fontFile)
19:12:24.141 forums:build: | ^
19:12:24.141 forums:build: 21 | }
19:12:24.142 forums:build: 22 |
19:12:24.142 forums:build: 23 | export const geistMonoRegular = readFile( {
19:12:24.142 forums:build: code: 'ERR_INVALID_ARG_TYPE'
19:12:24.142 forums:build: }
19:12:24.646 forums:build:
19:12:24.646 forums:build: > Build error occurred
19:12:24.648 forums:build: Error: Failed to collect page data for /api/og/post
19:12:24.650 forums:build: at ignore-listed frames {
19:12:24.650 forums:build: type: 'Error'
19:12:24.650 forums:build: }
19:12:24.750 forums:build: error: script "build" exited with code 1
19:12:24.750 forums:build: ERROR: command finished with error: command (/vercel/path0/apps/web) /bun1/bun run build exited (1)
19:12:24.750 forums#build: command (/vercel/path0/apps/web) /bun1/bun run build exited (1)
19:12:24.769
19:12:24.769 Tasks: 0 successful, 1 total
19:12:24.769 Cached: 0 cached, 1 total
19:12:24.769 Time: 52.589s
19:12:24.769 Summary: /vercel/path0/.turbo/runs/39MOCQtNgH8Sqwmq49HiJuQXp0C.json
19:12:24.769 Failed: forums#build
19:12:24.769
19:12:24.789 ERROR run failed: command exited (1)
19:12:24.805 Error: Command "cd ../.. && turbo run build --filter={apps/web}..." exited with 1
Pushed. The PR at https://github.com/basehub-ai/forums/pull/139 is updated.
Summary of the fix:
-
Root cause: The OG image routes used
process.cwd() + "node_modules/geist/dist/fonts/..."to load fonts, but in the monorepo on Vercel,node_modules/geistis hoisted to the workspace root whileprocess.cwd()points toapps/web/. -
First attempt (commit 1): Used
createRequire(import.meta.url).resolve("geist/font/mono")to dynamically find the geist package. This failed because Turbopack transformsrequire.resolve()at compile time into a numeric module ID (654304), not a file path string. -
Working fix (commit 2): Vendored the two TTF files (
GeistMono-Regular.ttf,GeistMono-Bold.ttf) intopublic/fonts/and load them viareadFile(join(process.cwd(), "public", "fonts", ...)). This uses the exact same pattern that already works forpublic/icon.svgin the home OG route —public/files are available on the Vercel serverless function filesystem.
Merge it
PR #139 has been squash-merged.
END OF POST