What happened?
LoHa output incompatible with ComfyUI — kernel size mismatch between hada_w1_a (1×1) and hada_w1_b (3×3)
Summary
When training a LoHa (LyCORIS) for SDXL using OneTrainer and then loading the resulting .safetensors in ComfyUI, the model fails with:
RuntimeError: self must be a matrix
The root cause is that OneTrainer saves LoHa convolution layer weights with inconsistent kernel sizes between hada_w1_a (1×1) and hada_w1_b (3×3). ComfyUI's LoHa loader expects both matrices to share the same kernel size, and its torch.mm() call cannot operate on the mismatched 4D tensors.
Environment
- OneTrainer: master branch, commit
4d239422 (ot_revision from safetensors metadata)
- ComfyUI: 0.28.0
- PyTorch: 2.9.1+cu130
- Python: 3.13.11
- Base model: SDXL 1.0 Base (
sd_xl_base_1.0.safetensors)
- LoHa config:
peft_type: LOHA
lora_rank: 16
lora_alpha: 8.0
layer_filter_preset: attn-mlp
output_model_format: KOHYA_LORA
output_dtype: FLOAT_16
- Dataset: 482 images at 512×132 (EU4 event illustrations)
- Training: 11 epochs completed (~4900 steps), smooth loss converged at 0.115
Reproduction
- Train a LoHa in OneTrainer with
peft_type: LOHA on any SDXL base model
- Copy the output
.safetensors to ComfyUI's models/loras/ directory
- In ComfyUI, create a workflow:
Load Checkpoint → Load LoRA → KSampler
- Set LoRA strength to 0.7
- Run the KSampler
Expected: LoHa loads and applies correctly.
Actual: ComfyUI logs hundreds of lora key not loaded warnings, then crashes with RuntimeError: self must be a matrix.
Error Details
ComfyUI error location: comfy/weight_adapter/loha.py, line 263
m1 = torch.mm(
comfy.model_management.cast_to_device(w1a, weight.device, intermediate_dtype),
comfy.model_management.cast_to_device(w1b, weight.device, intermediate_dtype),
)
torch.mm() requires both inputs to be 2D matrices, but OneTrainer saves them as 4D tensors with mismatched kernel sizes.
Root Cause Analysis
Tensor shapes in the OneTrainer output
Inspected using safetensors.torch.safe_open:
Layer: unet.conv_in
hada_w1_a: shape = torch.Size([320, 16, 1, 1]) # kernel = 1×1
hada_w1_b: shape = torch.Size([16, 4, 3, 3]) # kernel = 3×3 ⚠️ mismatch
Layer: unet.down_blocks.0.resnets.0.conv1
hada_w1_a: shape = torch.Size([320, 16, 1, 1]) # kernel = 1×1
hada_w1_b: shape = torch.Size([16, 320, 3, 3]) # kernel = 3×3 ⚠️ mismatch
Layer: unet.down_blocks.0.attentions.0.transformer_blocks.0.attn1.to_q
hada_w1_a: shape = torch.Size([out, 16, 1, 1]) # kernel = 1×1
hada_w1_b: shape = torch.Size([16, in, 3, 3]) # kernel = 3×3 ⚠️ mismatch
What ComfyUI expects
Looking at comfy/weight_adapter/loha.py:
-
CP decomposition path (line 234-261): uses torch.einsum with 4D tensors — works with any kernel size, but requires t1/t2 tensors (CP decomposition) which OneTrainer does not save.
-
Standard LoHa path (line 262-278): uses torch.mm(w1a, w1b) — requires both tensors to be 2D matrices (or at least have matching shapes that can be flattened consistently).
Since OneTrainer does not save CP decomposition tensors (t1/t2), ComfyUI falls through to the standard path, where torch.mm() fails on the 4D mismatched tensors.
Statistics from a real training output
- Total LoHa layers in the safetensors: 794
- Sampled 50 layers: 48 with kernel mismatch, 2 with matching kernels
- Almost all conv layers have
w1_a = 1×1 and w1_b = 3×3
This appears to be a fixed design choice in OneTrainer's LoHa implementation, not a per-layer configuration error.
ComfyUI Warning Logs (excerpt)
[WARNING] lora key not loaded: unet.up_blocks.0.attentions.2.transformer_blocks.8.attn2.to_out.0.hada_w2_a
[WARNING] lora key not loaded: unet.up_blocks.0.attentions.2.transformer_blocks.8.attn2.to_out.0.hada_w2_b
[WARNING] lora key not loaded: unet.up_blocks.0.attentions.2.transformer_blocks.8.attn2.to_q.alpha
[WARNING] lora key not loaded: unet.up_blocks.0.attentions.2.transformer_blocks.8.attn2.to_q.hada_w1_a
[WARNING] lora key not loaded: unet.up_blocks.0.attentions.2.transformer_blocks.8.attn2.to_q.hada_w1_b
... (hundreds more)
Metadata from the safetensors file
ss_base_model_version: sdxl_
modelspec.architecture: stable-diffusion-xl-v1-base/lora
modelspec.title: Stable Diffusion XL 1.0 Base LoRA
ot_revision: 4d239422
ot_branch: master
The metadata correctly identifies this as an SDXL LoRA, so the issue is not a model type mismatch.
Workaround
Switch from LoHa to LoCon (peft_type: LOCON) in the training config. LoCon output is handled correctly by ComfyUI's native LoRA loader.
Suggested Fixes
Any of the following would resolve the incompatibility:
-
Match kernel sizes: Save both hada_w1_a and hada_w1_b with the same kernel size (either both 1×1 for linear-only, or both 3×3 for full conv), matching the convention used by the official LyCORIS project.
-
Flatten to 2D: Save hada_w1_a as [out, rank] and hada_w1_b as [rank, in * k * k] (or vice versa), so ComfyUI's torch.mm() path works directly.
-
Add CP decomposition tensors: Save t1/t2 tensors so ComfyUI uses the torch.einsum path instead of torch.mm().
-
Document the limitation: If this is an intentional design choice, add a note in the wiki's LoRA page stating that LoHa output requires a custom ComfyUI node (similar to how OFTv2 is documented as needing Koratahiu/ComfyUI-OFTv2).
References
What did you expect would happen?
No response
Relevant log output
Generate and upload debug_report.log
No response
What happened?
LoHa output incompatible with ComfyUI — kernel size mismatch between
hada_w1_a(1×1) andhada_w1_b(3×3)Summary
When training a LoHa (LyCORIS) for SDXL using OneTrainer and then loading the resulting
.safetensorsin ComfyUI, the model fails with:The root cause is that OneTrainer saves LoHa convolution layer weights with inconsistent kernel sizes between
hada_w1_a(1×1) andhada_w1_b(3×3). ComfyUI's LoHa loader expects both matrices to share the same kernel size, and itstorch.mm()call cannot operate on the mismatched 4D tensors.Environment
4d239422(ot_revision from safetensors metadata)sd_xl_base_1.0.safetensors)peft_type: LOHAlora_rank: 16lora_alpha: 8.0layer_filter_preset: attn-mlpoutput_model_format: KOHYA_LORAoutput_dtype: FLOAT_16Reproduction
peft_type: LOHAon any SDXL base model.safetensorsto ComfyUI'smodels/loras/directoryLoad Checkpoint → Load LoRA → KSamplerExpected: LoHa loads and applies correctly.
Actual: ComfyUI logs hundreds of
lora key not loadedwarnings, then crashes withRuntimeError: self must be a matrix.Error Details
ComfyUI error location:
comfy/weight_adapter/loha.py, line 263torch.mm()requires both inputs to be 2D matrices, but OneTrainer saves them as 4D tensors with mismatched kernel sizes.Root Cause Analysis
Tensor shapes in the OneTrainer output
Inspected using
safetensors.torch.safe_open:What ComfyUI expects
Looking at
comfy/weight_adapter/loha.py:CP decomposition path (line 234-261): uses
torch.einsumwith 4D tensors — works with any kernel size, but requirest1/t2tensors (CP decomposition) which OneTrainer does not save.Standard LoHa path (line 262-278): uses
torch.mm(w1a, w1b)— requires both tensors to be 2D matrices (or at least have matching shapes that can be flattened consistently).Since OneTrainer does not save CP decomposition tensors (
t1/t2), ComfyUI falls through to the standard path, wheretorch.mm()fails on the 4D mismatched tensors.Statistics from a real training output
w1_a = 1×1andw1_b = 3×3This appears to be a fixed design choice in OneTrainer's LoHa implementation, not a per-layer configuration error.
ComfyUI Warning Logs (excerpt)
Metadata from the safetensors file
The metadata correctly identifies this as an SDXL LoRA, so the issue is not a model type mismatch.
Workaround
Switch from
LoHatoLoCon(peft_type: LOCON) in the training config. LoCon output is handled correctly by ComfyUI's native LoRA loader.Suggested Fixes
Any of the following would resolve the incompatibility:
Match kernel sizes: Save both
hada_w1_aandhada_w1_bwith the same kernel size (either both 1×1 for linear-only, or both 3×3 for full conv), matching the convention used by the official LyCORIS project.Flatten to 2D: Save
hada_w1_aas[out, rank]andhada_w1_bas[rank, in * k * k](or vice versa), so ComfyUI'storch.mm()path works directly.Add CP decomposition tensors: Save
t1/t2tensors so ComfyUI uses thetorch.einsumpath instead oftorch.mm().Document the limitation: If this is an intentional design choice, add a note in the wiki's LoRA page stating that LoHa output requires a custom ComfyUI node (similar to how OFTv2 is documented as needing Koratahiu/ComfyUI-OFTv2).
References
comfy/weight_adapter/loha.pyWhat did you expect would happen?
No response
Relevant log output
Generate and upload debug_report.log
No response