What's broken
dub@0.71.6 added a runtime dep on jsonpath@^1.2.1. jsonpath@1.2.x does sync fs.readFileSync at module load and uses Module._compile to regex-patch esprima at import time — this breaks under any bundler (Next.js/Turbopack, webpack server bundles, esbuild, Lambda zips). Importing dub from a bundled server entry now fails at build/cold-start, even if you never call the functions that use jsonpath.
0.71.5 is fine. 0.71.6 and 0.71.7 both fail.
Repro
Next.js 16 App Router, single API route:
// app/api/analytics/route.ts
import { Dub } from 'dub';
export async function POST() {
const dub = new Dub({ token: process.env.DUB_API_KEY });
return Response.json(await dub.analytics.retrieve({ event: 'clicks', interval: '24h', linkId: 'xyz' }));
}
pnpm add dub@0.71.7 && pnpm build
Collecting page data using 3 workers ...
Error: EBADF: bad file descriptor, fstat
at module evaluation (.next/server/chunks/[root-of-the-server]__*.js)
errno: -9, code: 'EBADF', syscall: 'fstat'
> Failed to collect page data for /api/analytics
Root cause
In the generated SDK, dist/commonjs/funcs/commissionsList.js and customersList.js do:
const jsonpath_1 = __importDefault(require("jsonpath"));
// ...
const nextCursor = jsonpath_1.default.value(responseData, "$[-1].id");
CommonJS doesn't tree-shake, so every consumer pays this import. And jsonpath@1.2.1 does this at load time:
// node_modules/jsonpath/lib/aesprim.js
var source = fs.readFileSync(require.resolve('esprima'), 'utf-8');
source = source.replace(/.../, '...');
new Module('aesprim')._compile(source, __filename);
// node_modules/jsonpath/lib/grammar.js
grammar.moduleInclude = fs.readFileSync(require.resolve("../include/module.js"));
grammar.actionInclude = fs.readFileSync(require.resolve("../include/action.js"));
Bundlers don't ship those sibling files next to the emitted chunk, so the resolves/reads fail. EBADF is what Turbopack's worker pool surfaces; webpack/Lambda surface as MODULE_NOT_FOUND or ENOENT. jsonpath@1.2.x is unmaintained (last release 2020) and won't be fixed upstream.
Suggested fix
The only usage is jsonpath.value(arr, "$[-1].id") — drop the dep and inline:
const nextCursor = Array.isArray(responseData) ? responseData.at(-1)?.id : undefined;
If you need real JSONPath later, jsonpath-plus is the bundler-safe alternative.
Workaround for consumers
Pin exactly: "dub": "0.71.5" (the caret in ^0.71.5 resolves up to 0.71.7).
Env
dub 0.71.6 / 0.71.7 broken, 0.71.5 ok. Next.js 16.2.1, Node 20, repro on macOS and Linux.
What's broken
dub@0.71.6added a runtime dep onjsonpath@^1.2.1.jsonpath@1.2.xdoes syncfs.readFileSyncat module load and usesModule._compileto regex-patchesprimaat import time — this breaks under any bundler (Next.js/Turbopack, webpack server bundles, esbuild, Lambda zips). Importingdubfrom a bundled server entry now fails at build/cold-start, even if you never call the functions that use jsonpath.0.71.5is fine.0.71.6and0.71.7both fail.Repro
Next.js 16 App Router, single API route:
pnpm add dub@0.71.7 && pnpm buildRoot cause
In the generated SDK,
dist/commonjs/funcs/commissionsList.jsandcustomersList.jsdo:CommonJS doesn't tree-shake, so every consumer pays this import. And
jsonpath@1.2.1does this at load time:Bundlers don't ship those sibling files next to the emitted chunk, so the resolves/reads fail.
EBADFis what Turbopack's worker pool surfaces; webpack/Lambda surface asMODULE_NOT_FOUNDorENOENT.jsonpath@1.2.xis unmaintained (last release 2020) and won't be fixed upstream.Suggested fix
The only usage is
jsonpath.value(arr, "$[-1].id")— drop the dep and inline:If you need real JSONPath later,
jsonpath-plusis the bundler-safe alternative.Workaround for consumers
Pin exactly:
"dub": "0.71.5"(the caret in^0.71.5resolves up to0.71.7).Env
dub0.71.6 / 0.71.7 broken, 0.71.5 ok. Next.js 16.2.1, Node 20, repro on macOS and Linux.