Dependency Constraints¶
Override and control package versions globally across your environment using UV constraint dependencies.
Overview¶
Constraints are global version restrictions that override what versions UV can choose when resolving dependencies:
- Pin specific versions - Lock PyTorch, NumPy, or other critical packages
- Prevent updates - Keep packages within a major version range
- Resolve conflicts - Force compatible versions when nodes disagree
- Establish baselines - Set version requirements before adding nodes
Constraints are stored in [tool.uv.constraint-dependencies] in your .cec/pyproject.toml and apply to all dependencies in the environment.
Constraints vs regular dependencies
- Regular dependencies (
cfd py add) - Packages you explicitly need installed - Constraints (
cfd constraint add) - Version restrictions without installation
Constraints don't install packages—they control what versions can be installed by other dependencies.
Before you begin¶
Make sure you have:
- An active environment —
cfd use <name>or use-e <name>flag - Understanding of version specifiers (
==,>=,<, etc.)
Understanding constraints¶
What are constraints?¶
Think of constraints as meta-dependencies—they don't install packages themselves, but they restrict what versions UV can choose when resolving other dependencies.
Example scenario:
# Without constraints
cfd node add node-a # Installs torch==2.0.0
cfd node add node-b # Error! Requires torch>=2.1.0
# With constraints
cfd constraint add "torch==2.1.0" # Set version requirement
cfd node add node-a # Uses torch==2.1.0 (constraint overrides node's requirement)
cfd node add node-b # Uses torch==2.1.0 (satisfied)
The constraint forces both nodes to use PyTorch 2.1.0, even though node-a requested 2.0.0.
How constraints work¶
When UV resolves dependencies:
- Reads constraints from
[tool.uv.constraint-dependencies] - Applies restrictions to all package resolution
- Rejects incompatible versions even if requested by dependencies
- Succeeds only if all constraints can be satisfied simultaneously
In pyproject.toml:
These constraints apply to:
- Packages added with cfd py add
- Dependencies from custom nodes (cfd node add)
- Transitive dependencies (dependencies of dependencies)
Adding constraints¶
Add version restrictions to your environment:
What happens:
- Updates pyproject.toml - Adds to
[tool.uv.constraint-dependencies] - No immediate installation - Constraints don't install packages
- Applied on next resolution - Takes effect when you add/update packages
Example output:
📦 Adding constraints: torch==2.1.0
✓ Added 1 constraint(s) to pyproject.toml
Run 'comfydock -e my-env constraint list' to view all constraints
Adding multiple constraints¶
Add several constraints at once:
All constraints are added together.
Common constraint patterns¶
Pin to exact version¶
Lock a package to a specific version:
Result: torch = "==2.1.0"
Use when: You need reproducibility or a specific feature version.
Minimum version requirement¶
Ensure at least a certain version:
Result: numpy = ">=1.24.0"
Use when: You need modern features or bug fixes.
Major version cap¶
Allow minor/patch updates but prevent major version changes:
Result: pillow = ">=9.0.0,<10.0.0"
Use when: You want updates but need API stability.
Minor version cap¶
Lock to a specific minor version series:
Result: transformers = ">=4.30.0,<4.31.0"
Use when: You need patch updates but want to avoid feature changes.
PyTorch with CUDA version¶
Pin PyTorch to a specific CUDA build:
Result: torch = "==2.1.0+cu121"
Use when: You need a specific CUDA version for GPU compatibility.
Quoting version specifiers
Always quote constraints with special characters:
Updating existing constraints¶
Adding a constraint for an existing package updates it:
# First time
cfd constraint add "torch==2.0.0"
# Update to newer version
cfd constraint add "torch==2.1.0"
The second command replaces the first constraint. No duplicates are created.
Listing constraints¶
View all active constraints in your environment:
Example output:
Constraint dependencies in 'my-project':
• torch==2.1.0+cu121
• numpy>=1.24.0
• pillow>=9.0.0,<10.0.0
• opencv-python>=4.8.0
When no constraints exist¶
Output:
This is normal—most environments start without constraints and add them as needed.
Removing constraints¶
Remove version restrictions you no longer need:
What happens:
- Removes from pyproject.toml - Deletes from
[tool.uv.constraint-dependencies] - Package stays installed - Doesn't uninstall the package
- Next resolution is unconstrained - Future updates can choose any version
Example output:
Removing multiple constraints¶
Remove several constraints at once:
Removing non-existent constraints¶
ComfyDock safely handles constraints that don't exist:
Output:
🗑 Removing constraints: nonexistent
Warning: constraint 'nonexistent' not found
✓ Removed 0 constraint(s) from pyproject.toml
No error—the operation is idempotent.
Common use cases¶
Resolving node conflicts¶
Two nodes require incompatible package versions:
# Check what's conflicting
cfd node add node-b
# Output:
# ✗ Dependency conflict:
# torch==2.0.0 (required by node-a)
# conflicts with torch>=2.1.0 (required by node-b)
# Add constraint to force compatible version
cfd constraint add "torch==2.1.0"
# Now both nodes can install
cfd node add node-b --yes
See Resolving Node Conflicts for detailed conflict resolution strategies.
Establishing a compatibility baseline¶
Set constraints before adding nodes to prevent conflicts:
# Set your environment's foundation
cfd constraint add "torch==2.1.0+cu121"
cfd constraint add "numpy>=1.24.0,<2.0.0"
cfd constraint add "pillow>=9.0.0,<10.0.0"
# Now add nodes—they'll all use compatible versions
cfd node add comfyui-impact-pack
cfd node add comfyui-controlnet-aux
cfd node add comfyui-animatediff
This prevents conflicts by establishing version requirements upfront.
Locking PyTorch backend¶
Pin PyTorch to a specific CUDA version for your GPU:
# CUDA 12.1
cfd constraint add "torch==2.1.0+cu121"
cfd constraint add "torchvision==0.16.0+cu121"
cfd constraint add "torchaudio==2.1.0+cu121"
# Or CUDA 11.8
cfd constraint add "torch==2.1.0+cu118"
cfd constraint add "torchvision==0.16.0+cu118"
cfd constraint add "torchaudio==2.1.0+cu118"
# Or CPU-only
cfd constraint add "torch==2.1.0+cpu"
cfd constraint add "torchvision==0.16.0+cpu"
cfd constraint add "torchaudio==2.1.0+cpu"
Ensures all nodes use the same PyTorch backend.
Preventing unwanted upgrades¶
Keep packages stable while allowing patch updates:
# Lock to transformers 4.30.x
cfd constraint add "transformers>=4.30.0,<4.31.0"
# Lock to diffusers 0.21.x
cfd constraint add "diffusers>=0.21.0,<0.22.0"
Useful when newer versions have breaking changes or regressions.
Forcing modern package versions¶
Ensure all dependencies use recent versions:
# Require Python 3.10+ features
cfd constraint add "numpy>=1.24.0"
cfd constraint add "scipy>=1.11.0"
cfd constraint add "pillow>=10.0.0"
Prevents nodes from dragging in old dependencies.
Working with local package indexes¶
Combine with custom indexes for air-gapped environments:
# Add constraint for package from custom index
cfd constraint add "internal-package>=1.0.0"
# The constraint applies when installing from custom index
cfd py add internal-package
Advanced patterns¶
Constraints from file¶
Create a constraints file for reusable baselines:
constraints.txt:
torch==2.1.0+cu121
torchvision==0.16.0+cu121
torchaudio==2.1.0+cu121
numpy>=1.24.0,<2.0.0
pillow>=9.0.0,<10.0.0
opencv-python>=4.8.0
transformers>=4.30.0,<5.0.0
diffusers>=0.21.0,<1.0.0
Apply all constraints:
# Read file and add each constraint
while IFS= read -r constraint; do
[[ "$constraint" =~ ^#.*$ || -z "$constraint" ]] && continue
cfd constraint add "$constraint"
done < constraints.txt
Or add manually:
Environment-specific constraints¶
Different environments with different GPU capabilities:
# Development machine (CUDA 12.1)
cfd -e dev-env constraint add "torch==2.1.0+cu121"
# Production server (CUDA 11.8)
cfd -e prod-env constraint add "torch==2.1.0+cu118"
# CPU-only testing
cfd -e test-env constraint add "torch==2.1.0+cpu"
Temporary constraints for testing¶
Test if a version works before committing:
# Add constraint
cfd constraint add "experimental-pkg==0.1.0-beta"
# Test installation
cfd node add test-node
# If it doesn't work, remove constraint
cfd constraint remove experimental-pkg
# Try different version
cfd constraint add "experimental-pkg==0.0.9"
Constraints with node installation¶
Apply constraint only for specific node installation:
# Add constraint before node
cfd constraint add "torch>=2.1.0"
cfd node add cuda-heavy-node
# Remove constraint after (if not needed globally)
cfd constraint remove torch
Troubleshooting¶
Constraint blocks all installations¶
Error:
✗ Failed to add node
No version of 'torch' satisfies constraint torch==2.0.0
and requirement torch>=2.1.0 (from node-b)
Cause: Constraint is too restrictive for a dependency.
Solution:
-
Check constraint:
-
Remove or relax constraint:
-
Retry installation:
Package still uses old version¶
Scenario: You added a constraint but package version didn't change.
Cause: Constraints don't trigger reinstallation—they only affect future resolutions.
Solution:
Force re-resolution with repair:
This will sync packages to satisfy the new constraints.
Conflicting constraints¶
Error:
Cause: You have multiple constraints for the same package with incompatible versions.
Solution:
-
List constraints:
-
Identify duplicates or conflicts (shouldn't happen normally, but check)
-
Remove and re-add with correct version:
Constraint not taking effect¶
Scenario: Added constraint but node still installs different version.
Cause: Constraint syntax might be incorrect or package name mismatch.
Solution:
-
Verify constraint was added:
-
Check package name spelling:
-
Check version specifier syntax:
Repair fails after adding constraint¶
Error:
Cause: Constraint is incompatible with existing dependencies.
Solution:
-
Check what's installed:
-
Remove conflicting constraint:
-
Try repair again:
-
If still fails, check node conflicts:
See Node Conflicts for detailed resolution steps.
How it works¶
Behind the scenes¶
When you run cfd constraint add:
- Reads pyproject.toml - Loads current configuration
- Updates or adds constraint - Modifies
[tool.uv.constraint-dependencies] - Writes to disk - Saves changes to
.cec/pyproject.toml - Tracks in git - Changes ready to commit
Example pyproject.toml:
[tool.uv.constraint-dependencies]
torch = "==2.1.0+cu121"
numpy = ">=1.24.0,<2.0.0"
pillow = ">=9.0.0,<10.0.0"
Constraints vs dependencies¶
Dependencies ([project.dependencies]):
- Install packages in your environment
- Show up in cfd py list
- Required for your code to run
- Added with cfd py add
Constraints ([tool.uv.constraint-dependencies]):
- Restrict versions during resolution
- Don't install anything themselves
- Applied to all dependencies
- Added with cfd constraint add
Example showing both:
[project.dependencies]
# These packages ARE installed
requests = ">=2.28.0"
[tool.uv.constraint-dependencies]
# These are version restrictions applied during resolution
urllib3 = ">=1.26.0,<2.0.0" # Constrains requests' dependency
Even though you didn't explicitly add urllib3 as a dependency, the constraint affects what version requests can use for its urllib3 dependency.
UV resolution with constraints¶
UV's resolution algorithm:
- Collects requirements from:
[project.dependencies]- Node groups (
[project.optional-dependencies]) -
Transitive dependencies
-
Applies constraints from
[tool.uv.constraint-dependencies] -
Finds compatible versions that satisfy all requirements + constraints
-
Fails if impossible to satisfy everything simultaneously
Example:
[project.dependencies]
node-a-dep = "*" # Wants torch>=2.0.0
[project.optional-dependencies]
"node/node-b" = ["torch>=2.1.0"]
[tool.uv.constraint-dependencies]
torch = "==2.1.0"
Resolution:
- node-a-dep wants torch>=2.0.0 → 2.1.0 satisfies
- node-b wants torch>=2.1.0 → 2.1.0 satisfies
- Constraint forces torch==2.1.0 → Final version: 2.1.0 ✓
Relationship to repair¶
The cfd repair command re-syncs your environment to match pyproject.toml:
# Add constraint
cfd constraint add "torch==2.1.0"
# Constraint is in pyproject.toml but not applied yet
# Apply constraint by repairing
cfd repair --yes
This triggers UV to re-resolve with the new constraint and install the correct versions.
Best practices¶
Start broad, narrow as needed¶
Begin with minimal constraints:
# Start with no constraints
cfd node add node-a node-b node-c
# Only add constraints when conflicts arise
# (ComfyDock will tell you if there's a conflict)
Add constraints reactively rather than preemptively.
Use version ranges over exact pins¶
Prefer ranges for flexibility:
# More flexible (allows patch updates)
cfd constraint add "numpy>=1.24.0,<2.0.0"
# Less flexible (locked to exact version)
cfd constraint add "numpy==1.24.3"
Use exact pins only when necessary (PyTorch CUDA versions, known bugs).
Document your constraints¶
Add comments to your pyproject.toml explaining why:
[tool.uv.constraint-dependencies]
# Pin PyTorch for CUDA 12.1 compatibility with RTX 4090
torch = "==2.1.0+cu121"
# Prevent numpy 2.0 due to breaking changes in node-x
numpy = ">=1.24.0,<2.0.0"
# Lock pillow to v9 due to regression in v10 loading certain formats
pillow = ">=9.5.0,<10.0.0"
Helps future you understand the reasoning.
Keep constraints in version control¶
Commit constraint changes with descriptive messages:
# Add constraint
cfd constraint add "torch==2.1.0+cu121"
# Commit with context
cfd commit -m "Pin PyTorch to 2.1.0+cu121 for CUDA 12.1 support"
Review constraints periodically¶
Constraints can become outdated:
# List all constraints
cfd constraint list
# Remove ones that are no longer needed
cfd constraint remove old-package
# Update versions to more recent ranges
cfd constraint add "numpy>=1.26.0,<2.0.0"
Test after changing constraints¶
Verify environment still works:
# Change constraint
cfd constraint add "torch==2.2.0"
# Apply changes
cfd repair --yes
# Test ComfyUI starts
cfd run
Catch issues early before committing.
Next steps¶
- Py Commands - Manage Python dependencies
- Node Conflicts - Resolve conflicts between custom nodes
- Version Control - Commit and track constraint changes
- Environment Repair - Apply constraints with repair