Skip to content

Commit 4d30a03

Browse files
Mary HippMary Hipp
authored andcommitted
error handling
1 parent 1d0b315 commit 4d30a03

File tree

1 file changed

+65
-4
lines changed

1 file changed

+65
-4
lines changed

invokeai/frontend/web/src/features/controlLayers/store/canvasWorkflowSlice.ts

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,24 @@ const validateCanvasWorkflow = (workflow: WorkflowV3, templates: Templates): Val
6161
return Boolean(template && template.tags.includes(OUTPUT_TAG));
6262
});
6363

64-
if (inputNodes.length !== 1) {
65-
throw new Error('A canvas workflow must include exactly one input node.');
64+
if (inputNodes.length === 0) {
65+
throw new Error('A canvas workflow must include at least one input node with the "canvas-workflow-input" tag.');
6666
}
6767

68-
if (outputNodes.length !== 1) {
69-
throw new Error('A canvas workflow must include exactly one output node.');
68+
if (inputNodes.length > 1) {
69+
throw new Error(
70+
`A canvas workflow must include exactly one input node, but found ${inputNodes.length}. Remove extra input nodes.`
71+
);
72+
}
73+
74+
if (outputNodes.length === 0) {
75+
throw new Error('A canvas workflow must include at least one output node with the "canvas-workflow-output" tag.');
76+
}
77+
78+
if (outputNodes.length > 1) {
79+
throw new Error(
80+
`A canvas workflow must include exactly one output node, but found ${outputNodes.length}. Remove extra output nodes.`
81+
);
7082
}
7183

7284
const inputNode = inputNodes[0]!;
@@ -88,6 +100,55 @@ const validateCanvasWorkflow = (workflow: WorkflowV3, templates: Templates): Val
88100
throw new Error('Canvas output node must accept an image input field named "image".');
89101
}
90102

103+
// Validate that all nodes have valid templates
104+
for (const node of invocationNodes) {
105+
const template = templates[node.data.type];
106+
if (!template) {
107+
throw new Error(
108+
`Node "${node.data.label || node.id}" uses invocation type "${node.data.type}" which is not available. This workflow may have been created with a different version of InvokeAI.`
109+
);
110+
}
111+
}
112+
113+
// Validate that required fields without connections have values
114+
const edges = workflow.edges.filter((edge) => edge.type === 'default');
115+
for (const node of invocationNodes) {
116+
if (node.type !== 'invocation') {
117+
continue;
118+
}
119+
120+
const template = templates[node.data.type];
121+
if (!template) {
122+
continue; // Already validated above
123+
}
124+
125+
for (const [fieldName, fieldTemplate] of Object.entries(template.inputs)) {
126+
// Skip if field is connected (will get value from connection)
127+
const isConnected = edges.some((edge) => edge.target === node.id && edge.targetHandle === fieldName);
128+
if (isConnected) {
129+
continue;
130+
}
131+
132+
// Check if field is required
133+
if (fieldTemplate.required) {
134+
const fieldInstance = node.data.inputs[fieldName];
135+
if (!fieldInstance) {
136+
throw new Error(
137+
`Node "${node.data.label || node.id}" is missing required field "${fieldTemplate.title || fieldName}".`
138+
);
139+
}
140+
141+
// Check if field has a value (not null/undefined/empty)
142+
const value = fieldInstance.value;
143+
if (value === null || value === undefined || value === '') {
144+
throw new Error(
145+
`Node "${node.data.label || node.id}" has required field "${fieldTemplate.title || fieldName}" with no value. Please provide a value or connect this field.`
146+
);
147+
}
148+
}
149+
}
150+
}
151+
91152
return { inputNodeId: inputNode.id, outputNodeId: outputNode.id };
92153
};
93154

0 commit comments

Comments
 (0)