Published on

Stable Diffusion LoRA 로드 오류 - safetensors·메타데이터 복구

Authors

서드파티 LoRA를 받아서 stable-diffusion-webui(A1111) 또는 ComfyUI에 넣었는데, 목록에 안 뜨거나 적용이 안 되거나, 로드 시점에 에러가 터지는 경우가 자주 있습니다. 특히 safetensors 기반 LoRA는 파일 자체는 멀쩡해 보여도 메타데이터가 비어 있거나 손상되어 UI가 정보를 읽지 못하는 일이 많습니다.

이 글에서는 LoRA 로드 오류를 증상별로 분류하고, safetensors 헤더/메타데이터를 검증·복구·재패킹하는 실전 절차를 다룹니다. 마지막에는 자동 점검 스크립트와, A1111·ComfyUI에서 다시 인식시키는 방법까지 정리합니다.

내부적으로는 “모델 파일 로더가 기대하는 키가 없다”, “차원(rank)·타깃 모듈이 맞지 않는다”, “캐시/인덱스가 꼬였다”, “파일이 부분 다운로드됐다” 같은 케이스가 대부분입니다.

가장 흔한 증상과 원인 매핑

1) UI에 LoRA가 아예 표시되지 않는다

  • 파일 확장자는 맞는데 목록에 없음
  • 새로고침/재시작해도 안 나옴

가능 원인

  • 파일이 LoRA 폴더가 아닌 다른 위치에 있음
  • 확장자가 .safetensors지만 실제로는 다른 포맷이거나 손상
  • 파일명에 특수문자/공백이 문제를 일으키는 플러그인 조합
  • A1111의 LoRA 캐시(태그/메타) 인덱싱이 꼬임

2) 표시되지만 적용해도 변화가 없다

  • 프롬프트에 LoRA를 넣어도 결과가 동일
  • 가중치를 올려도 효과가 미미

가능 원인

  • LoRA가 학습된 베이스 모델 계열이 다름 (SD1.5용 LoRA를 SDXL에 적용 등)
  • rank/alpha는 정상인데 타깃 모듈이 현재 UNet/텍스트 인코더와 불일치
  • 적용 위치(UNet만/TE만) 설정이 맞지 않음

3) 로드시 에러가 발생한다

자주 보이는 패턴

  • RuntimeError 또는 KeyError로 특정 키를 찾지 못함
  • safetensors 로드 단계에서 헤더 파싱 실패

가능 원인

  • 파일이 부분 다운로드되어 safetensors 헤더가 깨짐
  • 메타데이터가 비정상(너무 크거나, JSON이 깨짐)
  • 툴/확장(extensions) 간 LoRA 포맷 기대치가 다름

관련해서 “버전/시그니처를 못 맞춰 생기는 오류”는 다른 생태계에서도 흔합니다. TypeScript 데코레이터에서 시그니처 해석이 꼬일 때 해결하는 방식과 접근이 유사합니다: 원인 분해 → 최소 재현 → 로더가 기대하는 입력 형태로 맞추기. 참고: TS 5.5 데코레이터에서 Unable to resolve signature 해결

0단계: 파일 무결성부터 확인(가장 중요)

LoRA 파일이 깨졌는지 확인하려면 가장 먼저 크기와 해시를 봅니다.

  • 다운로드 페이지의 SHA256이 있다면 비교
  • 없으면 최소한 파일 크기가 비정상적으로 작지 않은지 확인

리눅스/맥

ls -lh "your_lora.safetensors"
shasum -a 256 "your_lora.safetensors"

윈도우 PowerShell

Get-Item "your_lora.safetensors" | Select-Object Name,Length
Get-FileHash "your_lora.safetensors" -Algorithm SHA256

파일이 손상된 상태면 메타데이터 복구를 시도해도 소용이 없습니다. 이 경우는 재다운로드가 최우선입니다.

1단계: safetensors 메타데이터를 읽어 진단하기

safetensors는 텐서 데이터와 별개로 메타데이터(JSON)를 담을 수 있습니다. UI/로더는 여기서

  • 학습 베이스(예: SD1.5, SDXL)
  • 태그, 트리거 워드
  • rank/alpha 힌트
  • 학습 툴 정보 같은 값을 읽어 표시하거나 자동 분류합니다.

다음 파이썬 스크립트로 메타데이터를 확인합니다.

# inspect_lora_meta.py
from safetensors import safe_open
import json
import sys

path = sys.argv[1]

with safe_open(path, framework="pt", device="cpu") as f:
    meta = f.metadata() or {}

print("== metadata keys ==")
for k in sorted(meta.keys()):
    v = meta[k]
    if v is None:
        print(k, "=", None)
    else:
        s = str(v)
        print(k, "=", s[:200] + ("..." if len(s) > 200 else ""))

print("\n== raw json dump ==")
print(json.dumps(meta, ensure_ascii=False, indent=2))

실행

python inspect_lora_meta.py "your_lora.safetensors"

메타데이터가 비어도 LoRA는 동작할 수 있다

중요한 점은, 메타데이터가 비어 있다고 해서 LoRA 텐서가 반드시 불량은 아닙니다.

  • 다만 A1111/ComfyUI가 “이게 LoRA인지”, “어떤 계열인지”를 추론하기 어려워서 표시/적용 흐름이 꼬일 수 있습니다.
  • 특히 관리용 확장(태그 기반 검색, 프리뷰 자동 생성)이 메타데이터를 전제로 할 때 문제가 커집니다.

2단계: LoRA 텐서 키를 확인해 SD1.5/SDXL 여부를 판별

메타데이터가 없거나 신뢰할 수 없다면, 텐서 키 이름을 보고 판별합니다.

# inspect_lora_keys.py
from safetensors import safe_open
import sys

path = sys.argv[1]

with safe_open(path, framework="pt", device="cpu") as f:
    keys = list(f.keys())

print("tensor key count:", len(keys))
print("sample keys:")
for k in keys[:50]:
    print("-", k)

# 매우 러프한 휴리스틱
is_sdxl = any("lora_unet" in k and "down" in k and "mid_block" in k for k in keys)
is_sd15 = any("lora_unet" in k and ("input_blocks" in k or "output_blocks" in k) for k in keys)

print("\nheuristic:")
print("looks like SDXL:", is_sdxl)
print("looks like SD1.5:", is_sd15)

실행

python inspect_lora_keys.py "your_lora.safetensors"

이 판별은 100% 정확하진 않지만, “SDXL 체크포인트에 SD1.5 LoRA를 억지로 적용” 같은 실수를 빠르게 잡는 데 도움이 됩니다.

3단계: 메타데이터만 복구(재작성)하기

파일 텐서는 정상인데 메타데이터가 깨져 UI가 오작동하는 경우, 텐서는 그대로 두고 메타데이터만 새로 써서 재패킹할 수 있습니다.

핵심 아이디어

  • safetensors는 “텐서 딕셔너리 + 메타데이터”로 저장
  • 기존 텐서를 모두 읽어 다시 저장하면서 메타데이터를 덮어씀

아래 스크립트는

  1. 기존 텐서를 전부 로드
  2. 새 메타데이터를 최소 세트로 구성
  3. 새 파일로 저장 을 수행합니다.
# repack_lora_with_meta.py
import sys
import torch
from safetensors import safe_open
from safetensors.torch import save_file

src = sys.argv[1]
dst = sys.argv[2]

# 1) 텐서 로드
state = {}
with safe_open(src, framework="pt", device="cpu") as f:
    for k in f.keys():
        state[k] = f.get_tensor(k)
    old_meta = f.metadata() or {}

# 2) 메타데이터 구성 (필요 최소)
# UI/툴마다 키 이름이 다를 수 있어, 범용적으로 "description" 계열을 둡니다.
new_meta = dict(old_meta)
new_meta.update({
    "ssmd_description": new_meta.get("ssmd_description") or "Repacked LoRA with repaired metadata",
    "ssmd_source": new_meta.get("ssmd_source") or "local-repack",
})

# 너무 큰 메타데이터는 로더/인덱서에서 문제를 일으킬 수 있어 제한 권장
for k, v in list(new_meta.items()):
    if v is None:
        continue
    if len(str(v)) > 10000:
        new_meta[k] = str(v)[:10000]

# 3) 저장
save_file(state, dst, metadata=new_meta)
print("saved:", dst)

실행

python repack_lora_with_meta.py "broken_meta.safetensors" "repacked.safetensors"

어떤 메타 키를 넣어야 하나?

정답은 “사용하는 도구가 무엇을 읽느냐”에 따라 달라집니다.

  • A1111 기본 LoRA 로딩 자체는 메타데이터 의존도가 낮은 편
  • 모델/LoRA 관리 확장, 프리뷰 생성기, 태그 검색기는 메타데이터를 적극적으로 사용

따라서 복구의 목표를 다음 중 무엇으로 둘지 먼저 정하세요.

  • “그냥 로드만 되면 됨”이면 최소 메타만
  • “태그/트리거/학습정보까지 복원”이면 원본 페이지(예: 배포 사이트)에서 정보를 다시 모아 채워 넣기

4단계: 아예 LoRA를 변환(재저장)해서 로더 호환성 맞추기

어떤 경우에는 메타데이터 문제가 아니라, LoRA 포맷이 툴 기대치와 다른 경우가 있습니다.

  • LoCon/LoHa/IA3 등 변형 LoRA
  • 텍스트 인코더 포함 여부
  • 특정 확장이 요구하는 키 네이밍

이때는 “그 툴에서 읽히는 형태로 변환 저장”이 필요합니다.

diffusers 기반으로 로드 후 재저장(상황에 따라)

환경이 복잡해질 수 있어 여기서는 뼈대만 제시합니다.

# pseudo_convert.py (개념 예시)
# 실제로는 사용 중인 파이프라인/LoRA 타입에 따라 로딩 API가 달라집니다.
import torch

# 1) diffusers pipeline 로드
# 2) LoRA 로드
# 3) 다시 safetensors로 export

print("Implement per your pipeline and LoRA type")

실무 팁

  • 변환은 “한 번에 해결”하려고 하기보다, 먼저 어떤 키에서 실패하는지 로그로 확인한 뒤 그 케이스에 맞는 변환기를 찾는 편이 빠릅니다.

5단계: A1111에서 LoRA 인덱스/캐시를 재생성

메타데이터를 고쳤는데도 UI에서 계속 이상하면, 대개는 캐시/인덱스가 남아서 그렇습니다.

체크리스트

  • WebUI 완전 종료 후 재실행
  • LoRA 폴더에 새 파일을 넣었으면 “새로고침” 버튼만으로는 부족할 때가 있음
  • LoRA 프리뷰/태그를 만드는 확장을 쓰는 경우, 확장 캐시도 삭제 필요

일반적으로 다음 경로 주변을 확인합니다.

  • models/Lora 아래 파일 배치가 맞는지
  • 확장이 만드는 json 인덱스 파일이 있는지

환경에 따라 캐시 경로가 다르므로, “LoRA tag cache”, “model hash cache” 같은 이름의 파일을 찾아 제거 후 재시작하는 방식이 안전합니다.

6단계: ComfyUI에서 로드 실패 시 체크 포인트

ComfyUI는 노드 그래프에서 LoRA 로더 노드가 실패할 때 메시지를 비교적 잘 보여줍니다.

자주 놓치는 것

  • LoRA 로더 노드가 참조하는 경로가 실제 파일명 변경으로 깨짐
  • SDXL용 LoRA인데 SD1.5 베이스 파이프에 연결
  • UNet/CLIP 로더 조합이 일치하지 않음

ComfyUI는 “그래프가 곧 설정”이라서, 같은 LoRA라도 어느 모델 로더에 붙이느냐가 중요합니다.

7단계: “메타데이터 복구”가 안 통하는 진짜 원인들

부분 다운로드/파일 손상

  • safetensors는 헤더가 깨지면 아예 열리지 않습니다.
  • 이 경우는 복구보다 재다운로드가 빠릅니다.

SD1.5 vs SDXL vs SD3 계열 불일치

  • LoRA는 베이스 아키텍처에 강하게 결합됩니다.
  • “로드는 되는데 효과가 없다”면 대부분 여기서 발생합니다.

확장 간 충돌

  • A1111 확장들이 LoRA 목록/메타를 후처리하면서 예외를 만들 수 있습니다.
  • 문제 재현 시에는 확장을 잠시 비활성화하고 기본 동작부터 확인하세요.

비슷한 방식으로, 복잡한 시스템에서 오류를 줄일 때는 “요청 폭주를 제어해 실패율을 낮추는” 접근도 도움이 됩니다. 모델 다운로드/메타 수집을 자동화해 배치로 돌린다면 재시도/백오프 설계가 효과적입니다. 참고: OpenAI API 429 폭탄 대응 실전 가이드 지수 백오프 큐잉 토큰 버짓으로 비용과 지연을 함께 줄이기

실전: 폴더 내 LoRA를 일괄 점검하고 문제 파일을 분류하는 스크립트

아래 스크립트는 지정 폴더의 .safetensors를 순회하며

  • 열기 가능 여부
  • 메타데이터 존재 여부
  • 텐서 키 개수 를 출력합니다.
# audit_loras.py
import os
import sys
from safetensors import safe_open

root = sys.argv[1]

items = []
for dirpath, _, filenames in os.walk(root):
    for fn in filenames:
        if fn.lower().endswith(".safetensors"):
            items.append(os.path.join(dirpath, fn))

items.sort()

for path in items:
    rel = os.path.relpath(path, root)
    try:
        with safe_open(path, framework="pt", device="cpu") as f:
            meta = f.metadata() or {}
            key_count = len(list(f.keys()))
        meta_ok = "yes" if len(meta) > 0 else "no"
        print(f"OK\tmeta:{meta_ok}\tkeys:{key_count}\t{rel}")
    except Exception as e:
        print(f"FAIL\t{rel}\t{type(e).__name__}: {e}")

실행 예시

python audit_loras.py "./models/Lora"

이 결과에서

  • FAIL은 파일 손상/포맷 불일치 가능성이 높고
  • OK meta:no는 “메타데이터 부재로 인덱싱/표시가 꼬일 수 있는 후보”입니다.

권장 복구 플로우(요약)

  1. 해시/파일 크기 확인으로 손상 여부부터 배제
  2. safetensors 메타데이터/키를 스크립트로 확인
  3. 메타데이터만 문제면 재패킹으로 복구
  4. 여전히 안 되면 베이스 모델 계열 불일치(SD1.5·SDXL) 점검
  5. A1111/ComfyUI 캐시·확장 영향 제거 후 재검증

마무리

LoRA 로드 오류는 겉으로는 “파일이 안 읽힌다”로 보이지만, 실제로는

  • 파일 무결성
  • 메타데이터 품질
  • 베이스 모델 계열 일치
  • 로더/확장/캐시의 기대치 가 얽힌 문제입니다.

특히 safetensors는 안전하고 빠르지만, 메타데이터가 깨지면 툴링 레이어에서 문제가 커지기 쉽습니다. 이 글의 스크립트로 먼저 “열리는지/메타가 있는지/키가 얼마나 있는지”를 정량적으로 확인하고, 재패킹으로 복구 가능한 케이스를 빠르게 분리하면 디버깅 시간이 크게 줄어듭니다.