Files
ComfyUI_frontend/src/composables
jaeone94 1349fffbce Feat/errors tab panel (#8807)
## Summary

Add a dedicated **Errors tab** to the Right Side Panel that displays
prompt-level, node validation, and runtime execution errors in a
unified, searchable, grouped view — replacing the need to rely solely on
modal dialogs for error inspection.

## Changes

- **What**:
  - **New components** (`errors/` directory):
- `TabErrors.vue` — Main error tab with search, grouping by class type,
and canvas navigation (locate node / enter subgraph).
- `ErrorNodeCard.vue` — Renders a single error card with node ID badge,
title, action buttons, and error details.
- `types.ts` — Shared type definitions (`ErrorItem`, `ErrorCardData`,
`ErrorGroup`).
- **`executionStore.ts`** — Added `PromptError` interface,
`lastPromptError` ref and `hasAnyError` computed getter. Clears
`lastPromptError` alongside existing error state on execution start and
graph clear.
- **`rightSidePanelStore.ts`** — Registered `'errors'` as a valid tab
value.
- **`app.ts`** — On prompt submission failure (`PromptExecutionError`),
stores prompt-level errors (when no node errors exist) into
`lastPromptError`. On both runtime execution error and prompt error,
deselects all nodes and opens the errors tab automatically.
- **`RightSidePanel.vue`** — Shows the `'errors'` tab (with ⚠ icon) when
errors exist and no node is selected. Routes to `TabErrors` component.
- **`TopMenuSection.vue`** — Highlights the action bar with a red border
when any error exists, using `hasAnyError`.
- **`SectionWidgets.vue`** — Detects per-node errors by matching
execution IDs to graph node IDs. Shows an error icon (⚠) and "See Error"
button that navigates to the errors tab.
- **`en/main.json`** — Added i18n keys: `errors`, `noErrors`,
`enterSubgraph`, `seeError`, `promptErrors.*`, and `errorHelp*`.
- **Testing**: 6 unit tests (`TabErrors.test.ts`) covering
prompt/node/runtime errors, search filtering, and clipboard copy.
- **Storybook**: 7 stories (`ErrorNodeCard.stories.ts`) for badge
visibility, subgraph buttons, multiple errors, runtime tracebacks, and
prompt-only errors.
- **Breaking**: None
- **Dependencies**: None — uses only existing project dependencies
(`vue-i18n`, `pinia`, `primevue`)

## Related Work

> **Note**: Upstream PR #8603 (`New bottom button and badges`)
introduced a separate `TabError.vue` (singular) that shows per-node
errors when a specific node is selected. Our `TabErrors.vue` (plural)
provides the **global error overview** — a different scope. The two tabs
coexist:
> - `'error'` (singular) → appears when a node with errors is selected →
shows only that node's errors
> - `'errors'` (plural) → appears when no node is selected and errors
exist → shows all errors grouped by class type
>
> A future consolidation of these two tabs may be desirable after design
review.

## Architecture

```
executionStore
├── lastPromptError: PromptError | null     ← NEW (prompt-level errors without node IDs)
├── lastNodeErrors: Record<string, NodeError>  (existing)
├── lastExecutionError: ExecutionError         (existing)
└── hasAnyError: ComputedRef<boolean>       ← NEW (centralized error detection)

TabErrors.vue (errors tab - global view)
├── errorGroups: ComputedRef<ErrorGroup[]>  ← normalizes all 3 error sources
├── filteredGroups                          ← search-filtered view
├── locateNode()                            ← pan canvas to node
├── enterSubgraph()                         ← navigate into subgraph
└── ErrorNodeCard.vue                       ← per-node card with copy/locate actions

types.ts
├── ErrorItem      { message, details?, isRuntimeError? }
├── ErrorCardData  { id, title, nodeId?, errors[] }
└── ErrorGroup     { title, cards[], priority }
```

## Review Focus

1. **Error normalization logic** (`TabErrors.vue` L75–150): Three
different error sources (prompt, node validation, runtime) are
normalized into a common `ErrorGroup → ErrorCardData → ErrorItem`
hierarchy. Edge cases to verify:
- Prompt errors with known vs unknown types (known types use localized
descriptions)
   - Multiple errors on the same node (grouped into one card)
   - Runtime errors with long tracebacks (capped height with scroll)

2. **Canvas navigation** (`TabErrors.vue` L210–250): The `locateNode`
and `enterSubgraph` functions navigate to potentially nested subgraphs.
The double `requestAnimationFrame` is required due to LiteGraph's
asynchronous subgraph switching — worth verifying this timing is
sufficient.

3. **Store getter consolidation**: `hasAnyError` replaces duplicated
logic in `TopMenuSection` and `RightSidePanel`. Confirm that the
reactive dependency chain works correctly (it depends on 3 separate
refs).

4. **Coexistence with upstream `TabError.vue`**: The singular `'error'`
tab (upstream, PR #8603) and our plural `'errors'` tab serve different
purposes but share similar naming. Consider whether a unified approach
is preferred.

## Test Results

```
✓ renders "no errors" state when store is empty
✓ renders prompt-level errors (Group title = error message)
✓ renders node validation errors grouped by class_type
✓ renders runtime execution errors from WebSocket
✓ filters errors based on search query
✓ calls copyToClipboard when copy button is clicked

Test Files  1 passed (1)
     Tests  6 passed (6)
```

## Screenshots (if applicable)
<img width="1238" height="1914" alt="image"
src="https://github.com/user-attachments/assets/ec39b872-cca1-4076-8795-8bc7c05dc665"
/>
<img width="669" height="1028" alt="image"
src="https://github.com/user-attachments/assets/bdcaa82a-34b0-46a5-a08f-14950c5a479b"
/>
<img width="644" height="1005" alt="image"
src="https://github.com/user-attachments/assets/ffef38c6-8f42-4c01-a0de-11709d54b638"
/>
<img width="672" height="505" alt="image"
src="https://github.com/user-attachments/assets/5cff7f57-8d79-4808-a71e-9ad05bab6e17"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8807-Feat-errors-tab-panel-3046d73d36508127981ac670a70da467)
by [Unito](https://www.unito.io)
2026-02-17 21:01:15 -08:00
..
2026-02-06 20:52:53 -08:00
2026-02-17 12:28:47 -08:00
2026-02-17 21:01:15 -08:00
2026-02-14 12:49:54 -08:00
2026-02-10 23:29:45 -08:00
2026-02-12 00:13:48 +01:00
2025-09-07 01:10:32 -07:00
2026-02-10 15:36:57 -05:00

Composables

This directory contains Vue composables for the ComfyUI frontend application. Composables are reusable pieces of logic that encapsulate stateful functionality and can be shared across components.

Table of Contents

Overview

Vue composables are a core part of Vue 3's Composition API and provide a way to extract and reuse stateful logic between multiple components. In ComfyUI, composables are used to encapsulate behaviors like:

  • State management
  • DOM interactions
  • Feature-specific functionality
  • UI behaviors
  • Data fetching

Composables enable a more modular and functional approach to building components, allowing for better code reuse and separation of concerns. They help keep your component code cleaner by extracting complex logic into separate, reusable functions.

As described in the Vue.js documentation, composables are:

Functions that leverage Vue's Composition API to encapsulate and reuse stateful logic.

Composable Architecture

The composable architecture in ComfyUI follows these principles:

  1. Single Responsibility: Each composable should focus on a specific concern
  2. Composition: Composables can use other composables
  3. Reactivity: Composables leverage Vue's reactivity system
  4. Reusability: Composables are designed to be used across multiple components

The following diagram shows how composables fit into the application architecture:

┌─────────────────────────────────────────────────────────┐
│                    Vue Components                        │
│                                                         │
│     ┌─────────────┐     ┌─────────────┐                 │
│     │ Component A │     │ Component B │                 │
│     └──────┬──────┘     └──────┬──────┘                 │
│            │                   │                        │
└────────────┼───────────────────┼────────────────────────┘
             │                   │
             ▼                   ▼
┌────────────┴───────────────────┴────────────────────────┐
│                    Composables                           │
│                                                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │
│  │ useFeatureA │  │ useFeatureB │  │ useFeatureC │      │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘      │
│         │                │                │             │
└─────────┼────────────────┼────────────────┼─────────────┘
          │                │                │
          ▼                ▼                ▼
┌─────────┴────────────────┴────────────────┴─────────────┐
│                    Services & Stores                     │
└─────────────────────────────────────────────────────────┘

Composable Categories

The following tables list ALL composables in the system as of 2026-01-30:

Auth

Composables for authentication and user management:

Composable Description
useCurrentUser Provides access to the current user information
useFirebaseAuthActions Handles Firebase authentication operations

Bottom Panel Tabs

Composables for terminal and bottom panel functionality:

Composable Description
useTerminal Core terminal functionality
useTerminalBuffer Manages terminal output buffer
useTerminalTabs Handles multiple terminal tab management

Element

Composables for DOM and element interactions:

Composable Description
useAbsolutePosition Handles element positioning
useCanvasPositionConversion Converts between canvas and DOM coordinates
useDomClipping Manages clipping of DOM elements
useResponsiveCollapse Manages responsive collapsing of elements

Functional

Utility composables for common patterns:

Composable Description
useChainCallback Chains multiple callbacks together

Manager

Composables for ComfyUI Manager integration (located in @/workbench/extensions/manager/composables):

Composable Description
useManagerStatePersistence Persists manager UI state
useManagerState Determines availability of manager UI modes
useManagerQueue Handles manager task queue state
useConflictAcknowledgment Tracks conflict dismissal state
useConflictDetection Orchestrates conflict detection workflow
useImportFailedDetection Handles import-failed conflict dialogs
useRegistrySearch Manages registry search UI state

Node Pack

Node package helpers live under @/workbench/extensions/manager/composables/nodePack:

Composable Description
useInstalledPacks Manages installed node packages
useMissingNodes Detects and handles missing nodes
useNodePacks Core node package functionality
usePackUpdateStatus Tracks package update availability
usePacksSelection Provides selection helpers for pack lists
usePacksStatus Aggregates status across multiple packs
useUpdateAvailableNodes Detects available updates for nodes
useWorkflowPacks Manages packages used in workflows

Node

Composables for node-specific functionality:

Composable Description
useNodeAnimatedImage Handles animated images in nodes
useNodeBadge Handles node badge display and interaction
useNodeCanvasImagePreview Canvas-based image preview for nodes
useNodeDragAndDrop Handles drag and drop for nodes
useNodeFileInput Manages file input widgets in nodes
useNodeImage Manages node image preview
useNodeImageUpload Handles image upload for nodes
useNodePaste Manages paste operations for nodes
useNodePricing Handles pricing display for nodes
useNodeProgressText Displays progress text in nodes
useWatchWidget Watches widget value changes

Settings

Composables for settings management:

Composable Description
useSettingSearch Provides search functionality for settings
useSettingUI Manages settings UI interactions

Sidebar Tabs

Composables for sidebar functionality:

Composable Description
useModelLibrarySidebarTab Manages the model library sidebar tab
useNodeLibrarySidebarTab Manages the node library sidebar tab
useWorkflowsSidebarTab Manages the workflows sidebar tab

Tree

Composables for tree structure operations:

Composable Description
useTreeFolderOperations Handles folder operations in tree views

Widgets

Composables for widget functionality:

Composable Description
useBooleanWidget Manages boolean widget interactions
useComboWidget Manages combo box widget interactions
useFloatWidget Manages float input widget interactions
useImagePreviewWidget Manages image preview widget
useImageUploadWidget Handles image upload widget
useIntWidget Manages integer input widget
useMarkdownWidget Handles markdown display widget
useProgressTextWidget Manages progress text widget
useRemoteWidget Handles remote widget connections
useStringWidget Manages string input widget

Root-level Composables

General-purpose composables:

Composable Description
useBrowserTabTitle Manages browser tab title updates
useCachedRequest Provides request caching functionality
useCanvasDrop Handles drop operations on canvas
useCivitaiModel Integrates with Civitai model API
useContextMenuTranslation Handles context menu translations
useCopy Provides copy functionality
useCopyToClipboard Manages clipboard operations
useCoreCommands Provides core command functionality
useDownload Handles file download operations
useErrorHandling Centralized error handling
useGlobalLitegraph Access to global LiteGraph instance
useLitegraphSettings Manages LiteGraph configuration
useManagerQueue Handles manager queue operations
usePaste Provides paste functionality
usePragmaticDragAndDrop Integrates Atlassian's drag-and-drop library
useProgressFavicon Updates favicon with progress indicator
useRefreshableSelection Manages refreshable selections
useRegistrySearch Searches the ComfyUI registry
useServerLogs Manages server log display
useTemplateWorkflows Manages template workflow loading, selection, and display
useTreeExpansion Handles tree node expansion state
useValueTransform Transforms values between formats
useWorkflowAutoSave Handles automatic workflow saving
useWorkflowPersistence Manages workflow persistence
useWorkflowValidation Validates workflow integrity

Usage Guidelines

When using composables in components, follow these guidelines:

  1. Import and call composables at the top level of the setup function
  2. Destructure returned values to use in your component
  3. Respect reactivity by not destructuring reactive objects
  4. Handle cleanup by using onUnmounted when necessary
  5. Use VueUse for common functionality instead of writing from scratch

Example usage:

<template>
  <div
    :class="{ dragging: isDragging }"
    @mousedown="startDrag"
    @mouseup="endDrag"
  >
    <img v-if="imageUrl" :src="imageUrl" alt="Node preview" />
  </div>
</template>

<script setup lang="ts">
import { useNodeDragAndDrop } from '@/composables/node/useNodeDragAndDrop'
import { useNodeImage } from '@/composables/node/useNodeImage'

// Use composables at the top level
const { isDragging, startDrag, endDrag } = useNodeDragAndDrop()
const { imageUrl, loadImage } = useNodeImage()

// Use returned values in your component
</script>

VueUse Library

ComfyUI leverages the VueUse library, which provides a collection of essential Vue Composition API utilities. Instead of implementing common functionality from scratch, prefer using VueUse composables for:

  • DOM event handling (useEventListener, useMouseInElement)
  • Element measurements (useElementBounding, useElementSize)
  • Asynchronous operations (useAsyncState, useFetch)
  • Animation and timing (useTransition, useTimeout, useInterval)
  • Browser APIs (useLocalStorage, useClipboard)
  • Sensors (useDeviceMotion, useDeviceOrientation)
  • State management (createGlobalState, useStorage)
  • ...and more

Examples:

// Instead of manually adding/removing event listeners
import { useEventListener } from '@vueuse/core'

useEventListener(window, 'resize', handleResize)

// Instead of manually tracking element measurements
import { useElementBounding } from '@vueuse/core'

const { width, height, top, left } = useElementBounding(elementRef)

// Instead of manual async state management
import { useAsyncState } from '@vueuse/core'

const { state, isReady, isLoading } = useAsyncState(
  fetch('https://api.example.com/data').then((r) => r.json()),
  { data: [] }
)

For a complete list of available functions, see the VueUse documentation.

Development Guidelines

When creating or modifying composables, follow these best practices:

  1. Name with use prefix: All composables should start with "use"
  2. Return an object: Composables should return an object with named properties/methods
  3. Handle cleanup: Use onUnmounted to clean up resources
  4. Document parameters and return values: Add JSDoc comments
  5. Test composables: Write unit tests for composable functionality
  6. Use VueUse: Leverage VueUse composables instead of reimplementing common functionality
  7. Implement proper cleanup: Cancel debounced functions, pending requests, and clear maps
  8. Use watchDebounced/watchThrottled: For performance-sensitive reactive operations

Composable Template

Here's a template for creating a new composable:

import { ref, computed, onMounted, onUnmounted } from 'vue'

/**
 * Composable for [functionality description]
 * @param options Configuration options
 * @returns Object containing state and methods
 */
export function useExample(options = {}) {
  // State
  const state = ref({
    // Initial state
  })

  // Computed values
  const derivedValue = computed(() => {
    // Compute from state
    return state.value.someProperty
  })

  // Methods
  function doSomething() {
    // Implementation
  }

  // Lifecycle hooks
  onMounted(() => {
    // Setup
  })

  onUnmounted(() => {
    // Cleanup
  })

  // Return exposed state and methods
  return {
    state,
    derivedValue,
    doSomething
  }
}

Common Patterns

Composables in ComfyUI frequently use these patterns:

State Management

export function useState() {
  const count = ref(0)

  function increment() {
    count.value++
  }

  return {
    count,
    increment
  }
}

Event Handling with VueUse

import { useEventListener } from '@vueuse/core'

export function useKeyPress(key) {
  const isPressed = ref(false)

  useEventListener('keydown', (e) => {
    if (e.key === key) {
      isPressed.value = true
    }
  })

  useEventListener('keyup', (e) => {
    if (e.key === key) {
      isPressed.value = false
    }
  })

  return { isPressed }
}

Fetch & Load with VueUse

import { useAsyncState } from '@vueuse/core'

export function useFetchData(url) {
  const {
    state: data,
    isLoading,
    error,
    execute: refresh
  } = useAsyncState(
    async () => {
      const response = await fetch(url)
      if (!response.ok) throw new Error('Failed to fetch data')
      return response.json()
    },
    null,
    { immediate: true }
  )

  return { data, isLoading, error, refresh }
}

For more information on Vue composables, refer to the Vue.js Composition API documentation and the VueUse documentation.