Skip to content

fix(runtime): avoid infinite recursion in fetch for external URLs#4153

Merged
pi0 merged 1 commit intonitrojs:mainfrom
vamsi2246:fix/runtime-fetch-recursion
Mar 26, 2026
Merged

fix(runtime): avoid infinite recursion in fetch for external URLs#4153
pi0 merged 1 commit intonitrojs:mainfrom
vamsi2246:fix/runtime-fetch-recursion

Conversation

@vamsi2246
Copy link
Copy Markdown
Contributor

Description

The exported fetch function in src/runtime/internal/app.ts shadows the global fetch, causing it to recursively call itself when handling non-root-relative URLs (e.g., full URL strings, URL objects, or Request objects for external endpoints). This results in a RangeError: Maximum call stack size exceeded.

Root Cause

In the fetch() function, the fallback branch for external URLs calls fetch(resource, init) — which resolves to the local function itself rather than the native globalThis.fetch:

// Before (broken)
return fetch(resource, init);

Fix

Explicitly use globalThis.fetch to delegate external requests to the runtime's native fetch implementation:

// After (fixed)
return globalThis.fetch(resource, init);

Changes

  • src/runtime/internal/app.ts: Changed fetch(resource, init)globalThis.fetch(resource, init) on the external URL fallback path.
  • test/fixture/server/routes/fetch.ts: Added a regression test case using a URL object to verify external fetch does not recurse.

How to reproduce

Call fetch(new URL('https://example.com')) or fetch('https://example.com') from any Nitro route handler — it crashes with a stack overflow.

Linked Issues

N/A (discovered during code review)

The exported `fetch` function in `src/runtime/internal/app.ts` shadows
the global `fetch`, causing it to recursively call itself when handling
non-root-relative URLs (e.g., full URLs or URL objects). This results in
a `RangeError: Maximum call stack size exceeded`.

Fix: use `globalThis.fetch` explicitly for external requests.
Added regression test with URL object in test fixture.
@vamsi2246 vamsi2246 requested a review from pi0 as a code owner March 26, 2026 18:43
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 26, 2026

Someone is attempting to deploy a commit to the Nitro Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

The fetch function in the runtime now delegates to the global fetch implementation instead of recursively calling itself, preventing re-entry loops. A new test case verifies fetch functionality with URL objects.

Changes

Cohort / File(s) Summary
Runtime fetch implementation
src/runtime/internal/app.ts
Modified the recursive fetch call to use globalThis.fetch instead of the module's fetch function, preventing re-entry into the module's fetch handler.
Test fixtures
test/fixture/server/routes/fetch.ts
Added a new test case "nitro/runtime.fetch.url" that invokes runtimeFetch with a URL object instead of a string path to verify proper handling.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'fix(runtime): avoid infinite recursion in fetch for external URLs' follows conventional commits with a 'fix' type, scope 'runtime', and clearly describes the main change.
Description check ✅ Passed The PR description is comprehensive and directly addresses the changeset, explaining the root cause, the fix applied, and test coverage added.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Member

@pi0 pi0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thnx!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/fixture/server/routes/fetch.ts (1)

13-15: Add an absolute-string URL regression case to match the reported repro.

Nice coverage for URL objects; consider adding a "http://localhost/..." string case too, since that was also part of the stack-overflow repro path.

Suggested diff
     "nitro/runtime.fetch.url": await runtimeFetch(new URL("http://localhost/api/hello")).then(
       (res) => res.json()
     ),
+    "nitro/runtime.fetch.absoluteString": await runtimeFetch("http://localhost/api/hello").then(
+      (res) => res.json()
+    ),

As per coding guidelines: test/**/*.{ts,js}: Keep tests deterministic and environment-independent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/fixture/server/routes/fetch.ts` around lines 13 - 15, Add a
deterministic regression test using an absolute-string URL alongside the
existing URL object case: update the test to call runtimeFetch with the string
"http://localhost/api/hello" (e.g., add another map entry similar to
"nitro/runtime.fetch.url" but for the string case) and await its .then(res =>
res.json()) so the suite covers both new URL(...) and plain
"http://localhost/..." call paths; locate the call to runtimeFetch in the test
(the existing "nitro/runtime.fetch.url" entry) and add the analogous
string-based entry there.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/fixture/server/routes/fetch.ts`:
- Around line 13-15: Add a deterministic regression test using an
absolute-string URL alongside the existing URL object case: update the test to
call runtimeFetch with the string "http://localhost/api/hello" (e.g., add
another map entry similar to "nitro/runtime.fetch.url" but for the string case)
and await its .then(res => res.json()) so the suite covers both new URL(...) and
plain "http://localhost/..." call paths; locate the call to runtimeFetch in the
test (the existing "nitro/runtime.fetch.url" entry) and add the analogous
string-based entry there.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a877b280-87bb-4694-b1d5-2fc95e419451

📥 Commits

Reviewing files that changed from the base of the PR and between 75cc89d and 5efefb6.

📒 Files selected for processing (2)
  • src/runtime/internal/app.ts
  • test/fixture/server/routes/fetch.ts

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 26, 2026

Open in StackBlitz

npm i https://pkg.pr.new/nitro@4153

commit: 5efefb6

@pi0 pi0 merged commit 2888e8f into nitrojs:main Mar 26, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants