#5552 pages dev server throws 500 errors while serving many requests in parallel
No evidence of fix; race condition in asset serving pipeline under concurrent load remains unaddressed despite major version changes
Investigate concurrency handling in miniflare asset serving; add stress tests
Analysis Report
Issue Review: cloudflare/workers-sdk#5552
Summary
Pages dev server throws 500 "internal error" when handling many parallel requests during integration testing with headless Playwright browsers.
Findings
- Created: 2024-04-07
- Updated: 2025-10-30 (no new comments, possibly automated update)
- Version: 3.48.0 -> 4.60.0 (major version change)
- Component: pages, wrangler, miniflare
- Labels: bug, pages
- Comments: 0
Key Evidence
No merged PRs explicitly fix this issue - Searched for PRs mentioning #5552, found only a dependency bump (#11376) that mentions it but doesn't fix the core issue.
No changelog mentions - Neither wrangler nor miniflare changelogs reference issue #5552 as being fixed.
Code Analysis - The stack trace points to
pages-template-worker.ts:170with an "internal error" being caught and returned as 500. The error originates from the asset fetching chain:pages-template-worker.tshandles routing for Pages functions- When
next()is called and there are no more handlers, it falls back toenv.ASSETS.fetch() - The asset serving involves multiple services:
assets-kv.worker.ts,assets.worker.ts(from@cloudflare/workers-shared)
Reproducibility Details - Issue is well-documented with:
- Cross-platform reproduction (Windows, macOS, Linux)
- Affects all invocation modes (
--proxy,-- <command>, static directory) - Occurs intermittently under sustained parallel load (18 concurrent browsers)
- Appears equally likely for any static asset type (JS, images, fonts)
No related duplicates - No other open issues describe the same concurrent request problem.
Architecture changes - Since the issue was filed (wrangler 3.48.0), significant refactoring has occurred:
- Workers Assets simulator implemented in miniflare (#6403, #6525)
workers-sharedpackage refactored (#9407)- Dev architecture refactored (#11244)
However, none of these explicitly address concurrent request handling or the reported race condition.
Recommendation
Status: KEEP OPEN
Reasoning: This appears to be an unresolved race condition or resource exhaustion issue in the local development asset serving pipeline. Despite major version changes and architectural refactoring since the issue was reported, there's no evidence the specific concurrency issue has been addressed. The problem is well-documented, reproducible across multiple platforms, and represents a real pain point for teams running integration tests.
Action: Keep open for investigation. This likely requires:
- Adding concurrency stress tests to the Pages dev test suite
- Investigating potential race conditions in the asset fetching chain
- Checking for resource limits (file descriptors, connection pools) in the local proxy
Root Cause Analysis
Likely Cause
The error "internal error" being thrown suggests an unhandled exception in the asset serving pipeline under concurrent load. Based on the code architecture:
Asset Fetching Chain: When a request comes in for a static asset:
pages-template-worker.tsroutes the request- Calls
env.ASSETS.fetch()for static files - This goes through miniflare's
assets-kv.worker.ts->blobsService.fetch() - The blob service reads from disk via a
diskservice
Potential Race Points:
- File descriptor exhaustion: Multiple concurrent reads from disk storage service
- Service binding saturation: The
MAYBE_SERVICE_BLOBSfetcher may have connection limits - Response body consumption: The
cloneResponse()function creates new Response objects with the body stream - if streams aren't properly consumed under concurrent load, this could cause issues
Code Reference: The relevant file at
packages/wrangler/templates/pages-template-worker.ts:164-172:} else if (__FALLBACK_SERVICE__) { // There are no more handlers so finish with the fallback service (`env.ASSETS.fetch` in Pages' case) const response = await env[__FALLBACK_SERVICE__].fetch(request); return cloneResponse(response); }The error is thrown somewhere in the
env[__FALLBACK_SERVICE__].fetch(request)call chain.
Files That Would Need Investigation/Modification
packages/miniflare/src/plugins/assets/index.ts- Asset plugin configurationpackages/miniflare/src/workers/assets/assets-kv.worker.ts- KV-style asset fetchingpackages/wrangler/templates/pages-template-worker.ts- Error handling in the template workerpackages/miniflare/src/runtime/index.ts- Workerd runtime configuration (potential resource limits)
Proposed Solution
Option 1: Add Better Error Handling (Easy)
Wrap the asset fetch in try-catch to provide more diagnostic information:
// In pages-template-worker.ts
} else if (__FALLBACK_SERVICE__) {
try {
const response = await env[__FALLBACK_SERVICE__].fetch(request);
return cloneResponse(response);
} catch (error) {
console.error(`[pages-dev] Asset fetch failed for ${request.url}:`, error);
// Return a more informative error
return new Response(
JSON.stringify({
error: 'Asset fetch failed',
url: request.url,
details: error.message
}),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
}
Option 2: Add Retry Logic with Backoff (Medium)
// In pages-template-worker.ts
const fetchWithRetry = async (service: any, request: Request, maxRetries = 3) => {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await service.fetch(request);
} catch (error) {
lastError = error;
if (i < maxRetries - 1) {
await new Promise(r => setTimeout(r, Math.pow(2, i) * 10));
}
}
}
throw lastError;
};
Option 3: Investigate and Fix Root Cause in Miniflare (Hard)
This would require:
- Adding concurrency limits to the disk service
- Implementing connection pooling for service bindings
- Adding proper backpressure handling for the asset serving pipeline
Implementation Difficulty
Medium to Hard - The surface-level fix (better error handling) is easy, but properly fixing the race condition requires:
- Deep understanding of workerd's service binding implementation
- Potential changes to miniflare's runtime configuration
- Adding comprehensive concurrency tests
Testing Recommendations
Create a stress test fixture:
// test/pages-dev-concurrent.test.ts test('handles 50 concurrent asset requests', async () => { const requests = Array(50).fill(null).map(() => fetch('http://localhost:8788/static/bundle.js') ); const responses = await Promise.all(requests); expect(responses.every(r => r.status === 200)).toBe(true); });Add Playwright-based integration test:
- Spin up Pages dev server
- Launch multiple browser contexts
- Load a page with many assets simultaneously
- Assert no 500 errors occur
Add monitoring/logging for failed asset requests to better diagnose production issues.
Suggested Comment
Thank you for this detailed bug report. This issue appears to still be relevant in wrangler 4.x. The concurrent request handling in the Pages dev server's asset serving pipeline may have race conditions under high load.
Could someone from the team investigate:
- Whether the miniflare asset serving pipeline has any connection/resource limits that could be exhausted under concurrent load
- Adding concurrency stress tests to the Pages dev test suite
For users experiencing this issue: As a workaround, you may be able to reduce the number of concurrent test browsers or add retry logic in your test framework for asset requests.
Notes & Feedback (0)
No notes yet.