What Happened
When running browser tests, all large JavaScript files (>8KB) are truncated to exactly 8192 bytes by the in-process AMPHP test server, causing SyntaxError: Unexpected end of input and SyntaxError: Invalid or unexpected token in the browser.
The errors appear on all pages that load large JS assets (Livewire, Filament, Vite bundles). Pages where all JS happens to be <8KB pass silently.
Evidence
Capturing window.__pestBrowser.jsErrors (with filename/line/col) after waitForLoadState('networkidle') shows:
[
{ "message": "Uncaught SyntaxError: Unexpected end of input", "filename": ".../vendor/livewire/livewire.min.js", "lineno": 1, "colno": 8193 },
{ "message": "Uncaught SyntaxError: Unexpected end of input", "filename": ".../js/filament/support/support.js", "lineno": 1, "colno": 8193 },
{ "message": "Uncaught SyntaxError: Invalid or unexpected token", "filename": ".../js/filament/tables/tables.js", "lineno": 1, "colno": 8189 },
{ "message": "Uncaught SyntaxError: Unexpected end of input", "filename": ".../build/assets/app.js", "lineno": 1, "colno": 8001 }
]
colno: 8193 = file received exactly 8192 bytes (1 × 8KB AMPHP read chunk) before EOF.
Actual file sizes: livewire.min.js = 237KB, support.js = 140KB, app.js = 187KB.
Root Cause
In LaravelHttpServer::asset(), JS files go through this path:
= fopen('php://temp', 'r+');
= fread(, filesize()); // reads full file ✓
= ->rewriteAssetUrl(); // rewrites URLs ✓
fwrite(, ); // writes to php://temp ✓
rewind(); // rewinds ✓
return new Response(200, [...], new ReadableResourceStream()); // ← BUG
ReadableResourceStream from AMPHP sets the stream to non-blocking mode via stream_set_blocking($resource, false). For php://temp in non-blocking mode, the underlying read returns at most one 8192-byte chunk per read event, and AMPHP's fiber-based event loop does not schedule subsequent reads within the same request lifecycle — so only the first 8KB is ever delivered to Playwright/Chromium.
Non-JS files are also served via ReadableResourceStream on the original file handle but they do not cause parse errors (images, CSS, fonts don't fail silently in the same way), so the truncation is hidden.
Fix
Replace the stream-based response with a plain string response, bypassing async streaming entirely:
private function asset(string $filepath): Response
{
$rawContent = file_get_contents($filepath);
if ($rawContent === false) {
return new Response(404);
}
$mimeTypes = new MimeTypes();
$contentType = $mimeTypes->getMimeTypes(pathinfo($filepath, PATHINFO_EXTENSION));
$contentType = $contentType[0] ?? 'application/octet-stream';
if (str_ends_with($filepath, '.js')) {
$rawContent = $this->rewriteAssetUrl($rawContent);
}
return new Response(200, [
'Content-Type' => $contentType,
'Content-Length' => (string) strlen($rawContent),
], $rawContent);
}
This also fixes the 0-byte file crash described in #1635 (no need for the filesize() === 0 guard in PR #203).
A PR with this fix is available at pestphp/pest-plugin-browser.
How to Reproduce
Any Laravel + Pest browser test suite that loads JS files larger than 8KB (Livewire, Filament, any Vite bundle). Use assertNoJavaScriptErrors() and it will fail. Or capture errors manually:
test('no js errors', function () {
$page = visit('/');
$page->page()->waitForLoadState('networkidle');
$errors = $page->script('() => window.__pestBrowser?.jsErrors ?? []');
// colno ~8193 on large JS files
dump($errors);
});
Pest Version
4.3.1
PHP Version
8.5 (also reproducible on 8.4 / 8.3)
Operation System
Linux
Notes
Related to #1635 (0-byte file crash). This is a separate truncation bug affecting all files >8KB.
What Happened
When running browser tests, all large JavaScript files (>8KB) are truncated to exactly 8192 bytes by the in-process AMPHP test server, causing
SyntaxError: Unexpected end of inputandSyntaxError: Invalid or unexpected tokenin the browser.The errors appear on all pages that load large JS assets (Livewire, Filament, Vite bundles). Pages where all JS happens to be <8KB pass silently.
Evidence
Capturing
window.__pestBrowser.jsErrors(with filename/line/col) afterwaitForLoadState('networkidle')shows:[ { "message": "Uncaught SyntaxError: Unexpected end of input", "filename": ".../vendor/livewire/livewire.min.js", "lineno": 1, "colno": 8193 }, { "message": "Uncaught SyntaxError: Unexpected end of input", "filename": ".../js/filament/support/support.js", "lineno": 1, "colno": 8193 }, { "message": "Uncaught SyntaxError: Invalid or unexpected token", "filename": ".../js/filament/tables/tables.js", "lineno": 1, "colno": 8189 }, { "message": "Uncaught SyntaxError: Unexpected end of input", "filename": ".../build/assets/app.js", "lineno": 1, "colno": 8001 } ]colno: 8193= file received exactly 8192 bytes (1 × 8KB AMPHP read chunk) before EOF.Actual file sizes:
livewire.min.js= 237KB,support.js= 140KB,app.js= 187KB.Root Cause
In
LaravelHttpServer::asset(), JS files go through this path:ReadableResourceStreamfrom AMPHP sets the stream to non-blocking mode viastream_set_blocking($resource, false). Forphp://tempin non-blocking mode, the underlying read returns at most one 8192-byte chunk per read event, and AMPHP's fiber-based event loop does not schedule subsequent reads within the same request lifecycle — so only the first 8KB is ever delivered to Playwright/Chromium.Non-JS files are also served via
ReadableResourceStreamon the original file handle but they do not cause parse errors (images, CSS, fonts don't fail silently in the same way), so the truncation is hidden.Fix
Replace the stream-based response with a plain string response, bypassing async streaming entirely:
This also fixes the 0-byte file crash described in #1635 (no need for the
filesize() === 0guard in PR #203).A PR with this fix is available at pestphp/pest-plugin-browser.
How to Reproduce
Any Laravel + Pest browser test suite that loads JS files larger than 8KB (Livewire, Filament, any Vite bundle). Use
assertNoJavaScriptErrors()and it will fail. Or capture errors manually:Pest Version
4.3.1
PHP Version
8.5 (also reproducible on 8.4 / 8.3)
Operation System
Linux
Notes
Related to #1635 (0-byte file crash). This is a separate truncation bug affecting all files >8KB.