Skip to content

Commit 19b31e5

Browse files
committed
use execution_error field
1 parent fd13755 commit 19b31e5

File tree

10 files changed

+166
-203
lines changed

10 files changed

+166
-203
lines changed

src/components/queue/job/JobDetailsPopover.vue

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
107107
import { t } from '@/i18n'
108108
import { isCloud } from '@/platform/distribution/types'
109109
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
110-
import { api } from '@/scripts/api'
111110
import { useDialogService } from '@/services/dialogService'
112111
import { useExecutionStore } from '@/stores/executionStore'
113112
import { useQueueStore } from '@/stores/queueStore'
@@ -355,7 +354,6 @@ const { errorMessageValue, copyErrorMessage, reportJobError } =
355354
useJobErrorReporting({
356355
taskForJob,
357356
copyToClipboard,
358-
dialog,
359-
fetchApi: (url) => api.fetchApi(url)
357+
dialog
360358
})
361359
</script>

src/components/queue/job/useJobErrorReporting.ts

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,32 @@
11
import { computed } from 'vue'
22
import type { ComputedRef } from 'vue'
33

4-
import { fetchJobDetail } from '@/platform/remote/comfyui/jobs'
54
import type { ExecutionErrorWsMessage } from '@/schemas/apiSchema'
65
import type { TaskItemImpl } from '@/stores/queueStore'
76

87
type CopyHandler = (value: string) => void | Promise<void>
9-
type FetchApi = (url: string) => Promise<Response>
108

119
export type JobErrorDialogService = {
10+
showExecutionErrorDialog: (executionError: ExecutionErrorWsMessage) => void
1211
showErrorDialog: (
1312
error: Error,
1413
options?: {
1514
reportType?: string
1615
[key: string]: unknown
1716
}
1817
) => void
19-
showExecutionErrorDialog?: (executionError: ExecutionErrorWsMessage) => void
2018
}
2119

2220
type UseJobErrorReportingOptions = {
2321
taskForJob: ComputedRef<TaskItemImpl | null>
2422
copyToClipboard: CopyHandler
2523
dialog: JobErrorDialogService
26-
/** Optional fetch function to enable rich error dialogs with traceback */
27-
fetchApi?: FetchApi
2824
}
2925

3026
export const useJobErrorReporting = ({
3127
taskForJob,
3228
copyToClipboard,
33-
dialog,
34-
fetchApi
29+
dialog
3530
}: UseJobErrorReportingOptions) => {
3631
const errorMessageValue = computed(() => taskForJob.value?.errorMessage ?? '')
3732

@@ -41,23 +36,15 @@ export const useJobErrorReporting = ({
4136
}
4237
}
4338

44-
const reportJobError = async () => {
39+
const reportJobError = () => {
4540
const task = taskForJob.value
4641
if (!task) return
4742

48-
// Try to fetch rich error details if fetchApi is provided
49-
if (fetchApi && dialog.showExecutionErrorDialog) {
50-
const jobDetail = await fetchJobDetail(fetchApi, task.promptId)
51-
const executionError = jobDetail?.execution_error
52-
53-
if (executionError) {
54-
dialog.showExecutionErrorDialog({
55-
prompt_id: task.promptId,
56-
timestamp: jobDetail?.create_time ?? Date.now(),
57-
...executionError
58-
})
59-
return
60-
}
43+
// Use execution_error from list response if available (includes prompt_id, timestamp)
44+
const executionError = task.executionError
45+
if (executionError) {
46+
dialog.showExecutionErrorDialog(executionError as ExecutionErrorWsMessage)
47+
return
6148
}
6249

6350
// Fall back to simple error dialog

src/composables/queue/useJobMenu.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,15 @@ export function useJobMenu(
9393
if (message) await copyToClipboard(message)
9494
}
9595

96-
const reportError = async () => {
96+
const reportError = () => {
9797
const item = currentMenuItem()
9898
if (!item) return
9999

100-
// Try to fetch rich error details from job detail
101-
const jobDetail = await fetchJobDetail((url) => api.fetchApi(url), item.id)
102-
const executionError = jobDetail?.execution_error
100+
// Use execution_error from list response if available
101+
const executionError = item.taskRef?.executionError
103102

104103
if (executionError) {
105-
// Use rich error dialog with traceback, node info, etc.
106-
useDialogService().showExecutionErrorDialog({
107-
prompt_id: item.id,
108-
timestamp: jobDetail?.create_time ?? Date.now(),
109-
...executionError
110-
})
104+
useDialogService().showExecutionErrorDialog(executionError)
111105
return
112106
}
113107

src/platform/remote/comfyui/jobs/fetchers/fetchJobs.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ async function fetchJobsRaw(
4646
return { jobs: [], total: 0, offset: 0 }
4747
}
4848
const data = zJobsListResponse.parse(await res.json())
49-
return { jobs: data.jobs, total: data.total, offset }
49+
return { jobs: data.jobs, total: data.pagination.total, offset }
5050
} catch (error) {
5151
console.error('[Jobs API] Error fetching jobs:', error)
5252
return { jobs: [], total: 0, offset: 0 }
@@ -135,14 +135,17 @@ export async function fetchJobDetail(
135135
}
136136

137137
/**
138-
* Extracts workflow from job detail response
138+
* Extracts workflow from job detail response.
139+
* The workflow is nested at: workflow.extra_data.extra_pnginfo.workflow
139140
*/
140141
export function extractWorkflow(
141142
job: JobDetail | undefined
142143
): ComfyWorkflowJSON | undefined {
143144
// Cast is safe - workflow will be validated by loadGraphData -> validateComfyWorkflow
144-
// Path: extra_data.extra_pnginfo.workflow (at top level of job detail)
145-
return job?.extra_data?.extra_pnginfo?.workflow as
145+
const workflowData = job?.workflow as
146+
| { extra_data?: { extra_pnginfo?: { workflow?: unknown } } }
147+
| undefined
148+
return workflowData?.extra_data?.extra_pnginfo?.workflow as
146149
| ComfyWorkflowJSON
147150
| undefined
148151
}

src/platform/remote/comfyui/jobs/types/jobTypes.ts

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,25 @@ const zPreviewOutput = z
3030
})
3131
.passthrough() // Allow extra fields like nodeId, mediaType
3232

33+
/**
34+
* Execution error details for error jobs.
35+
* Contains the same structure as ExecutionErrorWsMessage from WebSocket.
36+
*/
37+
const zExecutionError = z
38+
.object({
39+
node_id: z.string(),
40+
node_type: z.string(),
41+
executed: z.array(z.string()).optional(),
42+
exception_message: z.string(),
43+
exception_type: z.string(),
44+
traceback: z.array(z.string()),
45+
current_inputs: z.unknown(),
46+
current_outputs: z.unknown()
47+
})
48+
.passthrough()
49+
50+
export type ExecutionError = z.infer<typeof zExecutionError>
51+
3352
/**
3453
* Raw job from API - uses passthrough to allow extra fields
3554
*/
@@ -41,9 +60,11 @@ const zRawJobListItem = z
4160
preview_output: zPreviewOutput.nullable().optional(),
4261
outputs_count: z.number().optional(),
4362
error_message: z.string().nullable().optional(),
63+
execution_error: zExecutionError.nullable().optional(),
64+
workflow_id: z.string().nullable().optional(),
4465
priority: z.number().optional()
4566
})
46-
.passthrough() // Allow extra fields like execution_time, workflow_id, update_time
67+
.passthrough()
4768

4869
/**
4970
* Job list item with priority always set (either from server or synthetic)
@@ -52,61 +73,41 @@ const zJobListItem = zRawJobListItem.extend({
5273
priority: z.number() // Always set: server-provided or synthetic (total - offset - index)
5374
})
5475

55-
/**
56-
* Extra data structure containing workflow
57-
* Note: workflow is z.unknown() because it goes through validateComfyWorkflow separately
58-
*/
59-
const zExtraData = z
60-
.object({
61-
extra_pnginfo: z
62-
.object({
63-
workflow: z.unknown()
64-
})
65-
.optional()
66-
})
67-
.passthrough()
68-
69-
/**
70-
* Execution error details for failed jobs.
71-
* Contains the same structure as ExecutionErrorWsMessage from WebSocket.
72-
*/
73-
const zExecutionError = z.object({
74-
node_id: z.string(),
75-
node_type: z.string(),
76-
executed: z.array(z.string()),
77-
exception_message: z.string(),
78-
exception_type: z.string(),
79-
traceback: z.array(z.string()),
80-
current_inputs: z.unknown(),
81-
current_outputs: z.unknown()
82-
})
83-
8476
/**
8577
* Job detail - returned by GET /api/jobs/{job_id} (detail endpoint)
8678
* Includes full workflow and outputs for re-execution and downloads
87-
*
88-
* Note: workflow is at extra_data.extra_pnginfo.workflow (not in a separate workflow object)
8979
*/
9080
export const zJobDetail = zRawJobListItem
9181
.extend({
92-
extra_data: zExtraData.optional(),
93-
prompt: z.record(z.string(), z.unknown()).optional(),
82+
workflow: z.unknown().optional(),
9483
outputs: zTaskOutput.optional(),
95-
execution_time: z.number().optional(),
96-
workflow_id: z.string().nullable().optional(),
97-
execution_error: zExecutionError.nullable().optional()
84+
update_time: z.number().optional(),
85+
execution_status: z.unknown().optional(),
86+
execution_meta: z.unknown().optional()
9887
})
9988
.passthrough()
10089

10190
/**
102-
* Jobs list response structure - raw from API (before synthetic priority)
91+
* Pagination info from API
92+
*/
93+
const zPaginationInfo = z
94+
.object({
95+
offset: z.number(),
96+
limit: z.number(),
97+
total: z.number(),
98+
has_more: z.boolean()
99+
})
100+
.passthrough()
101+
102+
/**
103+
* Jobs list response structure
103104
*/
104105
export const zJobsListResponse = z
105106
.object({
106107
jobs: z.array(zRawJobListItem),
107-
total: z.number()
108+
pagination: zPaginationInfo
108109
})
109-
.passthrough() // Allow extra fields like has_more, offset, limit
110+
.passthrough()
110111

111112
// ============================================================================
112113
// TypeScript Types (derived from Zod schemas)

src/stores/queueStore.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,13 @@ export class TaskItemImpl {
287287
return this.job.error_message ?? undefined
288288
}
289289

290+
/**
291+
* Execution error details if job failed with traceback
292+
*/
293+
get executionError() {
294+
return this.job.execution_error ?? undefined
295+
}
296+
290297
get workflow(): ComfyWorkflowJSON | undefined {
291298
// Workflow is only available after lazy loading via getWorkflowFromHistory
292299
return undefined

0 commit comments

Comments
 (0)