Nodes API Reference
Discomfort provides several node types that enable programmatic control within ComfyUI workflows. These nodes act as bridges between your Python code and ComfyUI's execution graph.
DiscomfortPort
The primary node for data exchange between Python and ComfyUI workflows.
Purpose
DiscomfortPort is the main interface node that operates in three distinct modes based on its connections:
- 🔌 INPUT Mode - No incoming connections, data flows from Python → ComfyUI
- 📤 OUTPUT Mode - No outgoing connections, data flows from ComfyUI → Python
- 🔄 PASSTHRU Mode - Both incoming and outgoing connections, data passes through
Node Properties
unique_id
(Required)
Unique identifier that links this port to your Python code.
- Type: String
- Purpose: Key used in
context.save()
andcontext.load()
operations - Rules: Must be unique across all DiscomfortPorts in a workflow
- Examples:
"input_image"
,"model"
,"final_result"
tags
(Optional)
User-defined tags for categorization and filtering.
- Type: String (comma-separated values)
- Purpose: Organizational metadata for complex workflows
- Examples:
"model,checkpoint"
,"temp,debug"
,"output,final"
Usage Examples
INPUT Mode (Python → ComfyUI)
# Python side
with discomfort.Context() as context:
image = tools.open_image_as_tensor("photo.jpg")
context.save("input_image", image)
await discomfort.run(["workflow.json"], context=context)
// Workflow side - DiscomfortPort with unique_id="input_image"
{
"id": 5,
"type": "DiscomfortPort",
"inputs": {}, // No incoming connections = INPUT mode
"widgets_values": ["input_image", ""],
"pos": [100, 200]
}
OUTPUT Mode (ComfyUI → Python)
# Python side
with discomfort.Context() as context:
results = await discomfort.run(["workflow.json"], context=context)
output_image = context.load("final_result")
tools.save_comfy_image_to_disk(output_image, "result.png")
// Workflow side - DiscomfortPort with unique_id="final_result"
{
"id": 10,
"type": "DiscomfortPort",
"inputs": {
"value": ["8", 0] // Connected to previous node
},
"widgets_values": ["final_result", ""],
"pos": [800, 400]
// No outputs defined = OUTPUT mode
}
PASSTHRU Mode (No interaction)
When in PASSTHRU Mode, the DiscomfortPort is inactive. The only thing it does it to return, as outputs, the inputs it receives.
DiscomfortPorts that get connected to other DiscomfortPorts are automatically assigned to PASSTHRU mode.
// Workflow side - DiscomfortPort with unique_id="debug_checkpoint"
{
"id": 7,
"type": "DiscomfortPort",
"inputs": {
"value": ["6", 0] // Input from previous node
},
"outputs": {
"0": ["8", 0] // Output to next node
},
"widgets_values": ["debug_checkpoint", "debug"],
"pos": [400, 300]
}
Data Type Handling
DiscomfortPort automatically handles various ComfyUI data types:
Pass-by-Value Types
Those are variable types that are handled directly. Discomfortstores their values in context, which can be freely manipulated on Python.
Examples:
- IMAGE - Tensor data stored directly
- LATENT - Latent space representations
- MASK - Binary masks
- STRING - Text data
- INT/FLOAT - Numeric values
Pass-by-Reference Types
Those are variable types that are handled indirectly, usually due to their size. Instead of saving them directly (which could be costly in terms of time/memory/compute), Discomfort saves the smallest possible workflow JSON that leads to their actual value in ComfyUI. These sub-workflows are retrieved when a ComfyUI workflow requires the variable, with the sub-workflow then being stitched into the workflow being run.
Examples:
- MODEL - Stored as workflow graphs for reconstruction
- CLIP - Text encoder models
- VAE - Variational autoencoders
- CONDITIONING - Text conditioning data
- CONTROL_NET - ControlNet models
Best Practices
- Use descriptive unique_ids:
"input_image"
instead of"img"
- Place INPUT ports early: At the beginning of workflows
- Place OUTPUT ports late: At the end of processing chains
- Use PASSTHRU for debugging: Connect INPUT to OUTPUT DiscomfortPorts when testing on ComfyUI
DiscomfortTestRunner
Specialized node for automated testing and validation workflows.
Purpose
The DiscomfortTestRunner enables automated testing by executing validation checks within ComfyUI workflows and reporting results back to Python.
This node can be used in ComfyUI as a testbed for Discomfort workflows. Just add a path/to/workflow containing DiscomfortPorts and it will expose the INPUT and OUTPUT DiscomfortPorts of that sub-workflow to your existing workflow.
Always make sure you get the unique_id
s right when using this node; if you do so, it should work well.
Internal Nodes
These nodes are automatically inserted by Discomfort during workflow execution and are not meant to be manually placed. They replace DiscomfortPorts at Discomfort's runtime, simply by changing the class of the node inside the prompt JSON that will be sent for ComfyUI processing.
DiscomfortContextLoader
Internal node that loads data from WorkflowContext.
Purpose
- Automatically replaces INPUT mode DiscomfortPorts during execution
- Loads data from the active context using the
unique_id
- Handles pass-by-value and pass-by-reference data appropriately
Automatic Insertion
# Original workflow has DiscomfortPort in INPUT mode
# Discomfort automatically replaces it with:
{
"id": 100,
"type": "DiscomfortContextLoader",
"inputs": {},
"widgets_values": ["input_image", "val"], // [unique_id, pass_by_method]
"pos": [100, 200]
}
Data Loading
- Pass-by-value: Loads serialized data directly
- Pass-by-reference: (NOT handled by the DiscomfortContextLoader; instead, handled by the run() method directly)
DiscomfortContextSaver
Internal node that saves data to WorkflowContext.
Purpose
- Automatically replaces OUTPUT mode DiscomfortPorts during execution
- Saves data to the active context using the
unique_id
- Enables data extraction after workflow completion
Automatic Insertion
# Original workflow has DiscomfortPort in OUTPUT mode
# Discomfort automatically replaces it with:
{
"id": 101,
"type": "DiscomfortContextSaver",
"inputs": {
"value": ["8", 0] // Connected to data source
},
"widgets_values": ["final_result", "val"], // [unique_id, pass_by_method]
"pos": [800, 400]
}
Data Saving
- Pass-by-value: Serializes and stores data directly
- Pass-by-reference: Stores minimal workflow graph for reconstruction
Error Handling
Common Node Errors
Missing unique_id
# Error: DiscomfortPort without unique_id
{
"id": 5,
"type": "DiscomfortPort",
"widgets_values": ["", ""] # Empty unique_id
}
# Fix: Always provide unique_id
{
"id": 5,
"type": "DiscomfortPort",
"widgets_values": ["my_data", ""]
}
Duplicate unique_ids
# Error: Two ports with same unique_id;
# the first one will be overwritten.
# Be strategic about your unique_id names!
# Port 1: unique_id = "image_data"
# Port 2: unique_id = "image_data" # Duplicate!
# Fix: Use unique identifiers
# Port 1: unique_id = "input_image"
# Port 2: unique_id = "output_image"
Best Practices for Robustness
- Validate unique_ids: Check for duplicates and naming conflicts
- Use descriptive names: Clear naming reduces confusion
- Test workflows: Use DiscomfortTestRunner for validation
Next Steps
- Discomfort Class - Main orchestration API
- WorkflowTools API - Workflow manipulation utilities
- WorkflowContext API - Data management and persistence
- Core Concepts - Understanding DiscomfortPorts