ComfyUI Error Patterns ā
How to add and manage ComfyUI workflow error patterns in the TypeScript-only error handling system.
šÆ The ONE PLACE for ComfyUI Errors ā
When you see an unclassified ComfyUI error in production, there is ONE PLACE to go:
š packages/core/src/errors/comfyui-error-enhancer.tsWhy TypeScript-Only? ā
Per ERROR_HANDLING_MODERNIZATION.md ADR:
- ComfyUI is an upstream fork - The less we modify it, the easier it is to merge updates
- ComfyUI builds are slow - 64 custom nodes = long rebuild times
- TypeScript is source of truth - One place to maintain error logic
- No Python changes needed - Add patterns without ComfyUI rebuild
Step-by-Step: Adding a New Pattern ā
Example Production Error ā
You see this in the event stream:
KeyError: 'url'
File "/workspace/ComfyUI/custom_nodes/emprops_comfy_nodes/nodes/emprops_image_loader.py", line 66, in load_image
print(f"[EmProps] Downloading from URL: {kwargs['url']}", flush=True)
~~~~~~^^^^^^^Step 1: Identify the Pattern ā
The key pattern is: KeyError: 'url'
Additional context:
- Exception type:
KeyError - Missing field:
url - Node:
emprops_image_loader
Step 2: Open THE ONE PLACE ā
code packages/core/src/errors/comfyui-error-enhancer.tsStep 3: Add Pattern Matcher ā
Find the // PATTERN MATCHERS section and add your pattern:
// KeyError: Missing required parameter/field (generic KeyError pattern)
// Example: "KeyError: 'url'" from emprops_image_loader.py:66
// Example: "KeyError: 'source_type'" from custom nodes
if (exceptionType.toLowerCase() === 'keyerror' || message.toLowerCase().includes('keyerror')) {
const fieldMatch = message.match(/KeyError:\s*['"]([^'"]+)['"]/i);
const fieldName = fieldMatch ? fieldMatch[1] : 'unknown';
return new ConnectorError(
FailureType.VALIDATION_ERROR,
FailureReason.MISSING_REQUIRED_FIELD,
`Missing required field '${fieldName}' in node ${error.node_type || 'unknown'}`,
false, // Not retryable - user must fix workflow
{
serviceType: 'comfyui',
node: error.node_id ? { id: error.node_id, type: error.node_type } : undefined,
component: context?.component,
workflow: context?.workflow,
jobId: context?.jobId,
suggestion: `Provide the required '${fieldName}' parameter in your workflow configuration`,
missingField: fieldName,
rawError: error
}
);
}Step 4: Build and Test ā
# Build core package
pnpm --filter=@emp/core build
# Create test file
cat > /tmp/test-keyerror.mjs << 'EOF'
import { ComfyUIErrorEnhancer } from '/Users/the_dusky/code/emerge/emerge-turbo/packages/core/dist/errors/comfyui-error-enhancer.js';
const error = {
exception_message: "KeyError: 'url'",
exception_type: 'KeyError',
node_id: '66',
node_type: 'emprops_image_loader'
};
const result = ComfyUIErrorEnhancer.enhance(error, {
component: 'Image Loader',
workflow: 'test-workflow'
});
console.log('ā
Type:', result.failureType);
console.log('ā
Reason:', result.failureReason);
console.log('ā
Message:', result.message);
console.log('ā
Suggestion:', result.context.suggestion);
console.log('ā
Missing Field:', result.context.missingField);
if (result.failureReason === 'missing_required_field' && result.context.missingField === 'url') {
console.log('\nā
PASS - KeyError correctly classified!');
} else {
console.error('\nā FAIL - Incorrect classification');
process.exit(1);
}
EOF
# Run test
node /tmp/test-keyerror.mjsStep 5: Done! ā ā
No Python changes, no ComfyUI rebuild required.
Pattern Order Matters ā
More specific patterns should come BEFORE generic patterns:
// ā
CORRECT ORDER:
// 1. Missing custom node (specific KeyError case)
if (message.match(/node .+ does not exist/i)) { ... }
// 2. Generic KeyError (generic pattern)
if (exceptionType === 'keyerror') { ... }// ā WRONG ORDER:
// 1. Generic KeyError (catches everything)
if (exceptionType === 'keyerror') { ... }
// 2. Missing custom node (never reached!)
if (message.match(/node .+ does not exist/i)) { ... }Common Error Patterns ā
GPU Out of Memory ā
// GPU Out of Memory
// Example: "CUDA out of memory. Tried to allocate 2.00 GiB"
if (message.includes('CUDA out of memory') ||
message.includes('GPU memory') ||
message.includes('CUDA error: out of memory')) {
return new ConnectorError(
FailureType.RESOURCE_LIMIT,
FailureReason.GPU_MEMORY_FULL,
'GPU ran out of memory during workflow execution',
true, // Retryable with reduced settings
{
serviceType: 'comfyui',
node: error.node_id ? { id: error.node_id, type: error.node_type } : undefined,
suggestion: 'Try reducing image size, batch size, or use a lower resolution. Current settings exceeded GPU capacity.',
rawError: error
}
);
}CPU/System RAM Out of Memory ā
// CPU/System RAM Out of Memory
if ((message.includes('out of memory') && !message.includes('CUDA')) ||
exceptionType.toLowerCase() === 'memoryerror' ||
message.includes('cannot allocate memory')) {
return new ConnectorError(
FailureType.RESOURCE_LIMIT,
FailureReason.OUT_OF_MEMORY,
'System RAM exhausted during workflow execution',
true,
{
serviceType: 'comfyui',
suggestion: 'Try reducing batch size, closing other applications, or use less memory-intensive settings',
rawError: error
}
);
}Missing Model ā
// Model not found
// Example: "Checkpoint 'sd_xl_base_1.0.safetensors' not found"
if (message.match(/checkpoint|model.+not found|does not exist/i)) {
const modelMatch = message.match(/'([^']+)'/);
const modelName = modelMatch ? modelMatch[1] : 'unknown';
return new ConnectorError(
FailureType.VALIDATION_ERROR,
FailureReason.MODEL_NOT_FOUND,
`Model '${modelName}' not found in ComfyUI`,
false,
{
serviceType: 'comfyui',
suggestion: `Install the model '${modelName}' or select a different model from the available list.`,
missingModel: modelName,
rawError: error
}
);
}Missing Custom Node ā
// Missing custom node (specific KeyError case)
// Example: "Node 'FooNode' does not exist"
if (message.match(/node .+ does not exist/i)) {
const nodeMatch = message.match(/node '?(\S+?)'? does not exist/i);
const nodeName = nodeMatch ? nodeMatch[1] : 'unknown';
return new ConnectorError(
FailureType.VALIDATION_ERROR,
FailureReason.UNSUPPORTED_OPERATION,
`Custom node '${nodeName}' is not installed`,
false,
{
serviceType: 'comfyui',
suggestion: `Install the '${nodeName}' custom node from ComfyUI Manager before running this workflow.`,
missingNode: nodeName,
rawError: error
}
);
}FailureType and FailureReason ā
FailureType (High-Level) ā
enum FailureType {
VALIDATION_ERROR // Input validation, missing fields
RESOURCE_LIMIT // OOM, disk space, GPU limits
MODEL_ERROR // Model not found, loading failed
CUSTOM_NODE_ERROR // Node missing or execution failed
SYSTEM_ERROR // Unknown/unexpected errors
TIMEOUT // Operation took too long
}FailureReason (Specific) ā
enum FailureReason {
// Validation
MISSING_REQUIRED_FIELD
INVALID_PARAMETER
TYPE_MISMATCH
// Resources
GPU_MEMORY_FULL
OUT_OF_MEMORY
DISK_SPACE_FULL
// Models
MODEL_NOT_FOUND
MODEL_LOAD_FAILED
// Custom Nodes
UNSUPPORTED_OPERATION // Node not installed
MISSING_CUSTOM_NODE
// System
UNKNOWN_ERROR
TIMEOUT
}Variable Interpolation ā
You can extract values from errors and include them in messages:
// Extract field name from KeyError
const fieldMatch = message.match(/KeyError:\s*['"]([^'"]+)['"]/i);
const fieldName = fieldMatch ? fieldMatch[1] : 'unknown';
// Use in message
`Missing required field '${fieldName}' in node ${error.node_type}`
// Use in context
{
suggestion: `Provide the required '${fieldName}' parameter`,
missingField: fieldName // Available in context.missingField
}What NOT to Do ā
ā Don't Modify Python (ComfyUI) ā
# ā BAD - Modifying upstream fork
# packages/comfyui/execution.py
if "keyerror" in exception_type.lower():
return StructuredError(code=ErrorCode.VALIDATION_REQUIRED_INPUT_MISSING)ā Don't Add ErrorCode Enums in Python ā
# ā BAD - Duplicating logic in Python
class ErrorCode(str, Enum):
VALIDATION_REQUIRED_INPUT_MISSING = "VALIDATION_REQUIRED_INPUT_MISSING"ā Do This Instead ā
// ā
GOOD - TypeScript-only (THE ONE PLACE)
// packages/core/src/errors/comfyui-error-enhancer.ts
if (exceptionType === 'keyerror') {
return new ConnectorError(...)
}Debugging ā
Check Classification ā
# 1. Trigger error in ComfyUI workflow
# 2. Check worker logs
docker logs <worker-container> -f | grep "Critical error"
# 3. Check monitor UI
open http://localhost:3001
# Click on failed job ā See error message and suggestionVerify Pattern Matching ā
// Add debug logging to enhancer
static enhance(error: ComfyUIError, context?: ComfyUIErrorContext): ConnectorError {
const message = error.exception_message || error.message || '';
console.log('[DEBUG] Classifying error:', {
message,
exceptionType: error.exception_type,
nodeType: error.node_type
});
// ... pattern matching ...
}Testing Strategy ā
Unit Test ā
// packages/core/src/__tests__/comfyui-error-enhancer.test.ts
import { describe, it, expect } from 'vitest';
import { ComfyUIErrorEnhancer } from '../errors/comfyui-error-enhancer';
describe('ComfyUIErrorEnhancer', () => {
it('should classify KeyError correctly', () => {
const error = {
exception_message: "KeyError: 'url'",
exception_type: 'KeyError',
node_id: '66',
node_type: 'emprops_image_loader'
};
const result = ComfyUIErrorEnhancer.enhance(error);
expect(result.failureReason).toBe('missing_required_field');
expect(result.context.missingField).toBe('url');
expect(result.message).toContain('url');
});
});Integration Test ā
# 1. Start local development environment
pnpm dev:local-redis
# 2. Start basic machine
pnpm machines:basic:local:up:build
# 3. Submit job with missing field (trigger KeyError)
curl -X POST http://localhost:3100/jobs \
-H "Content-Type: application/json" \
-d '{
"service": "comfyui",
"payload": {
"workflow": {
"nodes": {
"1": {
"class_type": "emprops_image_loader",
"inputs": {
// Missing "url" field - will trigger KeyError
}
}
}
}
}
}'
# 4. Check monitor UI for error message
open http://localhost:3001Best Practices ā
DO ā
ā Add code comments with examples ā Extract variable values (field names, model names, etc.) ā Provide actionable suggestions ā Test patterns before deploying ā Use specific FailureReason values
DON'T ā
ā Don't add Python error classification ā Don't use generic error messages ā Don't skip testing ā Don't put specific patterns after generic ones ā Don't create new FailureType/FailureReason without good reason
Related Documentation ā
- Error Handling Overview
- Non-Critical Error Handling
- ADR: Error Handling Modernization
- FailureType/FailureReason Reference
Quick Reference ā
// Template for new ComfyUI error pattern
// Location: packages/core/src/errors/comfyui-error-enhancer.ts
// {Description} - {What triggers it}
// Example: "{Actual error message from logs}"
if (exceptionType.toLowerCase() === '{type}' || message.includes('{pattern}')) {
// Extract variables if needed
const someValue = message.match(/{regex}/)?.[1] || 'unknown';
return new ConnectorError(
FailureType.{TYPE},
FailureReason.{REASON},
'{User-friendly message with ${someValue}}',
{retryable}, // true or false
{
serviceType: 'comfyui',
node: error.node_id ? { id: error.node_id, type: error.node_type } : undefined,
component: context?.component,
workflow: context?.workflow,
jobId: context?.jobId,
suggestion: '{How to fix it}',
{customField}: someValue,
rawError: error
}
);
}