Bug: CustomRequest crashes with TypeError: Invalid URL: [object Object]
Description
OpenNext replaces globalThis.Request with CustomRequest, which passes input straight through to workerd's native Request via super(input, init).
workerd's Request constructor only accepts a string, URL, or Request instance. If it gets a plain object, it coerces it to a string ("[object Object]"), tries to parse that as a URL, and crashes. Node.js is more lenient and reads .url from plain objects, which is why this is workerd-specific.
Any code that does new Request({ url, method, headers, body }) triggers the crash (e.g. third-party middleware like Clerk).
Reproduction
The bug in two lines (works in Node.js, crashes in workerd):
const obj = { url: "https://example.com", method: "GET", headers: {}, body: null };
new Request(obj); // TypeError: Invalid URL: [object Object]
Full runnable worker
worker.js:
export default {
async fetch(request) {
const obj = { url: request.url, method: "GET", headers: {}, body: null };
return new Response(new Request(obj).url);
},
};
wrangler.jsonc:
npx wrangler dev && curl http://localhost:8787/
# → TypeError: Invalid URL: [object Object]
Fix
Before calling super(), check what input is. Strings, URLs, and real Request instances pass through directly. For plain objects, extract .url and merge method/headers/body into init so properties aren't lost:
const OriginalRequest = globalThis.Request;
const CustomRequest = class extends OriginalRequest {
constructor(input: RequestInfo | URL, init?: RequestInit) {
// ... existing cache/body fixes ...
if (typeof input === "string" || input instanceof URL || input instanceof OriginalRequest) {
super(input, init);
} else {
const req = input as unknown as Request;
const merged = {
method: req.method,
headers: req.headers,
body: req.body,
...(req.body ? { duplex: "half" as const } : {}),
...init,
};
super(req.url, merged as RequestInit);
}
}
};
Environment
@opennextjs/cloudflare: 1.17.0
- Cloudflare Workers runtime (workerd)
Bug:
CustomRequestcrashes withTypeError: Invalid URL: [object Object]Description
OpenNext replaces
globalThis.RequestwithCustomRequest, which passesinputstraight through to workerd's nativeRequestviasuper(input, init).workerd's
Requestconstructor only accepts a string, URL, or Request instance. If it gets a plain object, it coerces it to a string ("[object Object]"), tries to parse that as a URL, and crashes. Node.js is more lenient and reads.urlfrom plain objects, which is why this is workerd-specific.Any code that does
new Request({ url, method, headers, body })triggers the crash (e.g. third-party middleware like Clerk).Reproduction
The bug in two lines (works in Node.js, crashes in workerd):
Full runnable worker
worker.js:wrangler.jsonc:{ "name": "repro", "main": "worker.js", "compatibility_date": "2024-09-23" }Fix
Before calling
super(), check whatinputis. Strings, URLs, and real Request instances pass through directly. For plain objects, extract.urland mergemethod/headers/bodyinto init so properties aren't lost:Environment
@opennextjs/cloudflare: 1.17.0