From 370003da9403f314732ccddaeb8fc947aae6117d Mon Sep 17 00:00:00 2001 From: jaeone94 <89377375+jaeone94@users.noreply.github.com> Date: Tue, 10 Mar 2026 05:15:04 +0900 Subject: [PATCH] fix: add isGraphReady guard to prevent premature graph access error logs (#9672) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Adds `isGraphReady` getter to `ComfyApp` and uses it in `executionErrorStore` guards to prevent false 'ComfyApp graph accessed before initialization' error logs during early store evaluation. ## Changes - **What**: Added `isGraphReady` boolean getter to `ComfyApp` that safely checks graph initialization without triggering the `rootGraph` getter's error log. Updated 5 guard sites in `executionErrorStore` to use `app.isGraphReady` instead of `app.rootGraph`. - **Why**: The `rootGraph` getter logs an error when accessed before initialization. Computed properties and watch callbacks in `executionErrorStore` are evaluated early (before graph init), causing false error noise in the console. ## Review Focus - `isGraphReady` is intentionally minimal — just `!!this.rootGraphInternal` — to avoid duplicating the error-logging behavior of `rootGraph` - The `watch(lastNodeErrors, ...)` callback now checks `isGraphReady` at the top and early-returns, consistent with the computed property pattern ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9672-fix-add-isGraphReady-guard-to-prevent-premature-graph-access-error-logs-31e6d73d365081be8e1fc77114ce9382) by [Unito](https://www.unito.io) Co-authored-by: Alexander Brown --- docs/release-process.md | 30 +++++++++++++++--------------- src/scripts/app.ts | 5 +++++ src/stores/executionErrorStore.ts | 10 +++++----- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/docs/release-process.md b/docs/release-process.md index 69e94de511..d204f1c5d3 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -4,15 +4,15 @@ All releases use `release-version-bump.yaml`. Effects differ by bump type: -| Bump | Target | Creates branches? | GitHub release | -|---|---|---|---| -| Minor | `main` | `core/` + `cloud/` for previous minor | Published, "latest" | -| Patch | `main` | No | Published, "latest" | -| Patch | `core/X.Y` | No | **Draft** (uncheck "latest") | -| Prerelease | any | No | Draft + prerelease | +| Bump | Target | Creates branches? | GitHub release | +| ---------- | ---------- | ------------------------------------- | ---------------------------- | +| Minor | `main` | `core/` + `cloud/` for previous minor | Published, "latest" | +| Patch | `main` | No | Published, "latest" | +| Patch | `core/X.Y` | No | **Draft** (uncheck "latest") | +| Prerelease | any | No | Draft + prerelease | **Minor bump** (e.g. 1.41→1.42): freezes the previous minor into `core/1.41` -and `cloud/1.41`, branched from the commit *before* the bump. Nightly patch +and `cloud/1.41`, branched from the commit _before_ the bump. Nightly patch bumps on `main` are convenience snapshots — no branches created. **Patch on `core/X.Y`**: publishes a hotfix draft release. Must not be marked @@ -52,11 +52,11 @@ branch has unreleased commits, it triggers a patch bump and drafts a PR to ## Workflows -| Workflow | Purpose | -|---|---| -| `release-version-bump.yaml` | Bump version, create Release PR | -| `release-draft-create.yaml` | Build + publish to GitHub/PyPI/npm | -| `release-branch-create.yaml` | Create `core/` + `cloud/` branches (minor/major) | -| `release-biweekly-comfyui.yaml` | Auto-patch + ComfyUI requirements PR | -| `pr-backport.yaml` | Cherry-pick fixes to stable branches | -| `cloud-backport-tag.yaml` | Tag cloud branch merges | +| Workflow | Purpose | +| ------------------------------- | ------------------------------------------------ | +| `release-version-bump.yaml` | Bump version, create Release PR | +| `release-draft-create.yaml` | Build + publish to GitHub/PyPI/npm | +| `release-branch-create.yaml` | Create `core/` + `cloud/` branches (minor/major) | +| `release-biweekly-comfyui.yaml` | Auto-patch + ComfyUI requirements PR | +| `pr-backport.yaml` | Cherry-pick fixes to stable branches | +| `cloud-backport-tag.yaml` | Tag cloud branch merges | diff --git a/src/scripts/app.ts b/src/scripts/app.ts index e16146c49a..b06eb69977 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -208,6 +208,11 @@ export class ComfyApp { return this.rootGraphInternal! } + /** Whether the root graph has been initialized. Safe to check without triggering error logs. */ + get isGraphReady(): boolean { + return !!this.rootGraphInternal + } + canvas!: LGraphCanvas dragOverNode: LGraphNode | null = null readonly canvasElRef = shallowRef() diff --git a/src/stores/executionErrorStore.ts b/src/stores/executionErrorStore.ts index dbe6935d44..baff3c427f 100644 --- a/src/stores/executionErrorStore.ts +++ b/src/stores/executionErrorStore.ts @@ -238,7 +238,7 @@ export const useExecutionErrorStore = defineStore('executionError', () => { /** Graph node IDs (as strings) that have errors in the current graph scope. */ const activeGraphErrorNodeIds = computed>(() => { const ids = new Set() - if (!app.rootGraph) return ids + if (!app.isGraphReady) return ids // Fall back to rootGraph when currentGraph hasn't been initialized yet const activeGraph = canvasStore.currentGraph ?? app.rootGraph @@ -287,7 +287,7 @@ export const useExecutionErrorStore = defineStore('executionError', () => { const activeMissingNodeGraphIds = computed>(() => { const ids = new Set() - if (!app.rootGraph) return ids + if (!app.isGraphReady) return ids const activeGraph = canvasStore.currentGraph ?? app.rootGraph @@ -357,7 +357,7 @@ export const useExecutionErrorStore = defineStore('executionError', () => { /** True if the node has errors inside it at any nesting depth. */ function isContainerWithInternalError(node: LGraphNode): boolean { - if (!app.rootGraph) return false + if (!app.isGraphReady) return false const execId = getExecutionIdByNode(app.rootGraph, node) if (!execId) return false return errorAncestorExecutionIds.value.has(execId) @@ -365,15 +365,15 @@ export const useExecutionErrorStore = defineStore('executionError', () => { /** True if the node has a missing node inside it at any nesting depth. */ function isContainerWithMissingNode(node: LGraphNode): boolean { - if (!app.rootGraph) return false + if (!app.isGraphReady) return false const execId = getExecutionIdByNode(app.rootGraph, node) if (!execId) return false return missingAncestorExecutionIds.value.has(execId) } watch(lastNodeErrors, () => { + if (!app.isGraphReady) return const rootGraph = app.rootGraph - if (!rootGraph) return clearAllNodeErrorFlags(rootGraph)