Restore lazy chunk failure classification

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
brennanb2025
2026-06-24 13:35:37 -07:00
co-authored by Orca
parent 64f2b92bc8
commit 22728950fa
2 changed files with 42 additions and 3 deletions
@@ -15,7 +15,7 @@ export function describeLazyChunkError(
return {
reloadKey: sanitizeReloadKey(reloadKey),
errorName,
errorCategory: classifyErrorCategory(messageClass),
errorCategory: classifyErrorCategory(messageClass, errorName),
messageClass
}
}
@@ -30,6 +30,7 @@ function classifyErrorName(error: unknown): string {
}
switch (error.name) {
case 'AggregateError':
case 'ChunkLoadError':
case 'DOMException':
case 'EvalError':
case 'Error':
@@ -62,6 +63,7 @@ function classifyErrorMessage(message: string): string {
normalized.includes('failed to fetch dynamically imported module') ||
normalized.includes('error loading dynamically imported module') ||
normalized.includes('importing a module script failed') ||
normalized.includes('failed to load module script') ||
normalized.includes('loading chunk') ||
normalized.includes('chunkloaderror') ||
normalized.includes('networkerror')
@@ -71,11 +73,11 @@ function classifyErrorMessage(message: string): string {
return 'unknown'
}
function classifyErrorCategory(messageClass: string): string {
function classifyErrorCategory(messageClass: string, errorName: string): string {
if (messageClass === 'syntax') {
return 'syntax'
}
if (messageClass === 'fetch') {
if (messageClass === 'fetch' || errorName === 'ChunkLoadError') {
return 'fetch'
}
return 'unknown'
@@ -13,6 +13,15 @@ const chunkFetchError = (): TypeError =>
new TypeError(
'Failed to fetch dynamically imported module: https://assets.example.test/chunks/panel.js?v=secret /Users/test/orca/panel.js'
)
const chunkLoadErrorByName = (): Error => {
const error = new Error('async route asset request failed')
error.name = 'ChunkLoadError'
return error
}
const failedModuleScriptError = (): TypeError =>
new TypeError(
'Failed to load module script: Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of "text/html".'
)
function markChunkReloadAttemptedInPriorRenderer(): void {
window.sessionStorage.setItem(RELOAD_RENDERER_BOOT_KEY, 'previous-renderer-boot')
@@ -330,6 +339,34 @@ describe('loadLazyWithRetry', () => {
expect(isLazyChunkLoadError(caught)).toBe(true)
})
it.each([
['ChunkLoadError name', chunkLoadErrorByName],
['failed module script message', failedModuleScriptError]
])('restarts the app for post-reload %s failures', async (_label, createError) => {
const reload = spyOnReload()
const api = stubPreloadRecoveryApi()
markChunkReloadAttemptedInPriorRenderer()
const factory = vi.fn(() => Promise.reject(createError()))
const loaded = loadLazyWithRetry(factory, { retries: 0, reloadKey: 'route-panel' })
let settled = false
void loaded.then(
() => {
settled = true
},
() => {
settled = true
}
)
await vi.advanceTimersByTimeAsync(5000)
expect(api.getVersion).toHaveBeenCalledTimes(1)
expect(api.restart).toHaveBeenCalledTimes(1)
expect(window.localStorage.getItem(APP_RESTART_GUARD_KEY)).toBe('1')
expect(reload).not.toHaveBeenCalled()
expect(settled).toBe(false)
})
it('does not restart the app for non-chunk lazy module failures after renderer reload', async () => {
const reload = spyOnReload()
const api = stubPreloadRecoveryApi()