fix: vite dynamic import fix#1097
Conversation
🚀 Preview Deployment
Preview will be automatically removed when this PR is closed. |
There was a problem hiding this comment.
4 issues found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/frontend/src/main.tsx">
<violation number="1" location="apps/frontend/src/main.tsx:42">
P2: Both the Vite preload error handler here and the `RouterError` component rely on the same `sessionStorage` key (`nao:chunk-error-reloaded`) to coordinate reload-loop prevention, but the key is duplicated rather than shared. `RouterError` keeps it in a local `RELOAD_FLAG` constant, while `main.tsx` hardcodes it in three places. If the two ever drift, the recovery paths will stop recognizing each other and can reintroduce repeated reloads. Consider exporting `RELOAD_FLAG` from `router-error.tsx` (or a shared constant) and reusing it here, since `main.tsx` already imports `RouterError` from that module.</violation>
<violation number="2" location="apps/frontend/src/main.tsx:42">
P2: The `vite:preloadError` handler should call `event.preventDefault()` before reloading or returning to stop the original import error from propagating. Per the Vite docs, if `preventDefault()` is not called, the error is still thrown. Currently the handler doesn't accept the event parameter, so the error can propagate into TanStack Router's `defaultErrorComponent` (briefly showing `RouterError`) before the reload completes, and on guarded subsequent events the error is thrown unhandled. Consider adding the event parameter and calling `event.preventDefault()` in both the guard-return path and before `window.location.reload()`.</violation>
<violation number="3" location="apps/frontend/src/main.tsx:42">
P2: The new `vite:preloadError` handler and the `setTimeout` cleanup directly access `sessionStorage` without `try/catch` guards. In browsers or embedded contexts where storage is blocked or sandboxed, these calls can throw a `SecurityError`, which would abort the recovery reload and leave an unhandled exception from the timeout. The project already handles this correctly in `router-error.tsx` (e.g., `hasReloadedForChunkError` wraps `sessionStorage.getItem` in `try/catch`), so the same guard should be applied here for consistent error recovery.</violation>
</file>
<file name="apps/frontend/src/components/router-error.tsx">
<violation number="1" location="apps/frontend/src/components/router-error.tsx:28">
P1: The error fallback UI directly accesses `error.message`, but TanStack Router passes the raw thrown value to `ErrorComponentProps`, which can be a string, object, or primitive rather than an `Error` instance. If a non-Error value reaches this component, the render will throw at runtime. Use the same normalization pattern already present in `isChunkLoadError` — for example, render the message as `error instanceof Error ? error.message : String(error)` — so the UI remains safe regardless of what the route or loader throws.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| <div className='flex h-full flex-1 flex-col items-center justify-center gap-4 p-6 text-center'> | ||
| <div className='max-w-md space-y-2'> | ||
| <h2 className='text-lg font-medium'>Something went wrong</h2> | ||
| <p className='text-sm text-muted-foreground'>{error.message}</p> |
There was a problem hiding this comment.
P1: The error fallback UI directly accesses error.message, but TanStack Router passes the raw thrown value to ErrorComponentProps, which can be a string, object, or primitive rather than an Error instance. If a non-Error value reaches this component, the render will throw at runtime. Use the same normalization pattern already present in isChunkLoadError — for example, render the message as error instanceof Error ? error.message : String(error) — so the UI remains safe regardless of what the route or loader throws.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/frontend/src/components/router-error.tsx, line 28:
<comment>The error fallback UI directly accesses `error.message`, but TanStack Router passes the raw thrown value to `ErrorComponentProps`, which can be a string, object, or primitive rather than an `Error` instance. If a non-Error value reaches this component, the render will throw at runtime. Use the same normalization pattern already present in `isChunkLoadError` — for example, render the message as `error instanceof Error ? error.message : String(error)` — so the UI remains safe regardless of what the route or loader throws.</comment>
<file context>
@@ -0,0 +1,85 @@
+ <div className='flex h-full flex-1 flex-col items-center justify-center gap-4 p-6 text-center'>
+ <div className='max-w-md space-y-2'>
+ <h2 className='text-lg font-medium'>Something went wrong</h2>
+ <p className='text-sm text-muted-foreground'>{error.message}</p>
+ </div>
+ <div className='flex gap-2'>
</file context>
| <p className='text-sm text-muted-foreground'>{error.message}</p> | |
| <p className='text-sm text-muted-foreground'>{error instanceof Error ? error.message : String(error)}</p> |
| defaultErrorComponent: RouterError, | ||
| }); | ||
|
|
||
| window.addEventListener('vite:preloadError', () => { |
There was a problem hiding this comment.
P2: Both the Vite preload error handler here and the RouterError component rely on the same sessionStorage key (nao:chunk-error-reloaded) to coordinate reload-loop prevention, but the key is duplicated rather than shared. RouterError keeps it in a local RELOAD_FLAG constant, while main.tsx hardcodes it in three places. If the two ever drift, the recovery paths will stop recognizing each other and can reintroduce repeated reloads. Consider exporting RELOAD_FLAG from router-error.tsx (or a shared constant) and reusing it here, since main.tsx already imports RouterError from that module.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/frontend/src/main.tsx, line 42:
<comment>Both the Vite preload error handler here and the `RouterError` component rely on the same `sessionStorage` key (`nao:chunk-error-reloaded`) to coordinate reload-loop prevention, but the key is duplicated rather than shared. `RouterError` keeps it in a local `RELOAD_FLAG` constant, while `main.tsx` hardcodes it in three places. If the two ever drift, the recovery paths will stop recognizing each other and can reintroduce repeated reloads. Consider exporting `RELOAD_FLAG` from `router-error.tsx` (or a shared constant) and reusing it here, since `main.tsx` already imports `RouterError` from that module.</comment>
<file context>
@@ -35,8 +36,19 @@ const router = createRouter({
+ defaultErrorComponent: RouterError,
});
+window.addEventListener('vite:preloadError', () => {
+ if (sessionStorage.getItem('nao:chunk-error-reloaded') === '1') {
+ return;
</file context>
| defaultErrorComponent: RouterError, | ||
| }); | ||
|
|
||
| window.addEventListener('vite:preloadError', () => { |
There was a problem hiding this comment.
P2: The vite:preloadError handler should call event.preventDefault() before reloading or returning to stop the original import error from propagating. Per the Vite docs, if preventDefault() is not called, the error is still thrown. Currently the handler doesn't accept the event parameter, so the error can propagate into TanStack Router's defaultErrorComponent (briefly showing RouterError) before the reload completes, and on guarded subsequent events the error is thrown unhandled. Consider adding the event parameter and calling event.preventDefault() in both the guard-return path and before window.location.reload().
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/frontend/src/main.tsx, line 42:
<comment>The `vite:preloadError` handler should call `event.preventDefault()` before reloading or returning to stop the original import error from propagating. Per the Vite docs, if `preventDefault()` is not called, the error is still thrown. Currently the handler doesn't accept the event parameter, so the error can propagate into TanStack Router's `defaultErrorComponent` (briefly showing `RouterError`) before the reload completes, and on guarded subsequent events the error is thrown unhandled. Consider adding the event parameter and calling `event.preventDefault()` in both the guard-return path and before `window.location.reload()`.</comment>
<file context>
@@ -35,8 +36,19 @@ const router = createRouter({
+ defaultErrorComponent: RouterError,
});
+window.addEventListener('vite:preloadError', () => {
+ if (sessionStorage.getItem('nao:chunk-error-reloaded') === '1') {
+ return;
</file context>
| defaultErrorComponent: RouterError, | ||
| }); | ||
|
|
||
| window.addEventListener('vite:preloadError', () => { |
There was a problem hiding this comment.
P2: The new vite:preloadError handler and the setTimeout cleanup directly access sessionStorage without try/catch guards. In browsers or embedded contexts where storage is blocked or sandboxed, these calls can throw a SecurityError, which would abort the recovery reload and leave an unhandled exception from the timeout. The project already handles this correctly in router-error.tsx (e.g., hasReloadedForChunkError wraps sessionStorage.getItem in try/catch), so the same guard should be applied here for consistent error recovery.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/frontend/src/main.tsx, line 42:
<comment>The new `vite:preloadError` handler and the `setTimeout` cleanup directly access `sessionStorage` without `try/catch` guards. In browsers or embedded contexts where storage is blocked or sandboxed, these calls can throw a `SecurityError`, which would abort the recovery reload and leave an unhandled exception from the timeout. The project already handles this correctly in `router-error.tsx` (e.g., `hasReloadedForChunkError` wraps `sessionStorage.getItem` in `try/catch`), so the same guard should be applied here for consistent error recovery.</comment>
<file context>
@@ -35,8 +36,19 @@ const router = createRouter({
+ defaultErrorComponent: RouterError,
});
+window.addEventListener('vite:preloadError', () => {
+ if (sessionStorage.getItem('nao:chunk-error-reloaded') === '1') {
+ return;
</file context>
Uh oh!
There was an error while loading. Please reload this page.