Comfy-Org / Comfy-Org/ComfyUI_frontend

Suppressed vite:preloadError makes dynamic imports resolve to undefined — workflow save fails silently

Open
#15,561 0 comments 0 reactions 1 assignee Claimed by @jtydhr88 View on GitHub
Potential Bug
Dominant language
TypeScript
Stars
2k
Forks
699
Avg merge
1d 7h
Merged PRs (30d)
490

Description

## Summary

`preventDefault()` on `vite:preloadError` makes a failed dynamic import resolve to `undefined` instead of rejecting. Every `const { X } = await import(...)` downstream then throws `Cannot destructure property 'X' of undefined`, and in `ComfyWorkflow.save()` / `saveAs()` that means **the save does not happen**. The user is told nothing.

Production, last 7 days, `@service:comfy-cloud-frontend`:

| Error | Events / 7d |
| --- | --- |
| `Failed to load subgraph blueprint TypeError: Cannot destructure property 'useWorkflowDraftStoreV2' \| 'ChangeTracker' \| 'useSettingStore'` | **482** |
| `[vite:preloadError]` on `workflowDraftStoreV2-.js` | **269** |

Two symptoms, one cause — and the chunk named in the second is the module destructured in the first. Two distinct build hashes appear (`-EBDT…`, `-DPjP…`), which is the signature of clients pinned to stale chunk URLs across redeploys.

## Mechanism

Vite's preload helper rethrows **only if the event was not cancelled**:

```js
// vite/src/node/plugins/importAnalysisBuild.ts — preload helper
.catch((err) => {
const e = new Event('vite:preloadError', { cancelable: true })
e.payload = err
window.dispatchEvent(e)
if (!e.defaultPrevented) throw err // <-- cancelled => swallowed
})
```

`src/App.vue:69` cancels it:

```js
window.addEventListener('vite:preloadError', (event) => {
event.preventDefault()
...
})
```

With the throw suppressed, the `.catch()` returns `undefined`, so `await import(...)` **resolves to `undefined`**. Then:

```ts
// src/platform/workflow/management/stores/comfyWorkflow.ts:111
const { useWorkflowDraftStoreV2 } =
await import('@/platform/workflow/persistence/stores/workflowDraftStoreV2')
```

throws `TypeError: Cannot destructure property 'useWorkflowDraftStoreV2' of 'undefined'`.

## Why it is worse than the error count suggests

**1. It breaks saving, not just loading.** Three of the affected sites are on the save path:

| Site | Method |
| --- | --- |
| `comfyWorkflow.ts:111` | `load()` |
| `comfyWorkflow.ts:170` | `save()` |
| `comfyWorkflow.ts:189` | `saveAs()` |

`save()` throws before `super.save()` is reached, so the workflow is not persisted.

**2. There is no user-facing signal.** The preload-error toast is commented out at `App.vue:96-101`, deliberately — third-party extensions trigger it often and the message was not actionable. That reasoning is sound for extension noise, but it also means a *core* save failure is silent.

**3. It is misattributed.** In the blueprint path the rejection is collected by `Promise.allSettled` and logged as `Failed to load subgraph blueprint` (`subgraphStore.ts:265`). The error text names subgraphs; the actual failure is a 404'd workflow-persistence chunk. Anyone triaging by message goes to the wrong subsystem — which is presumably why 482 events/7d have gone unfiled.

## Not a regression, and not the handler's fault

#8261 replaced a hard page-reload with logging, which was the right call — a forced reload on any chunk hiccup is worse. The gap is that suppressing the throw silently changed the *resolved value* of every dynamic import in the app, and the call sites destructure immediately.

## Suggested fix

The handler and the call sites want different things: the handler wants "don't reload the page", the call sites want "don't hand me `undefined`".

Cheapest correct fix is to stop destructuring a value that can now be `undefined`:

```ts
const mod = await import('@/platform/workflow/persistence/stores/workflowDraftStoreV2')
if (!mod) throw new Error('workflowDraftStoreV2 chunk failed to load — reload required')
const { useWorkflowDraftStoreV2 } = mod
```

Better, if the team wants this handled once rather than at ~155 `} = await import(` sites repo-wide: keep `preventDefault()` for the reload behaviour but re-throw a typed `ChunkLoadError` so callers fail loudly and a single handler can offer "reload to update". Saving is the one path that should surface it to the user, since the alternative is losing work silently.

## Reproduction

1. Load the app.
2. Deploy (chunk hashes change; the old files stop being served).
3. Without reloading, save the workflow, or open the subgraph blueprint list.
4. Console shows `[vite:preloadError]` and `Cannot destructure property …`; the save does not land and nothing is shown to the user.

Observed continuously in production rather than only at deploy boundaries, so the stale-URL window is not brief.

## Method note

These counts come from the RUM aggregate endpoint grouped by `@error.message`. Filtering on a quoted `@error.message:"..."` returns **0** for strings that are demonstrably present — worth knowing for anyone re-checking these numbers.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.