From b9516c6b62abc9837efa08d9f0d5505910fe34a0 Mon Sep 17 00:00:00 2001 From: Will Miao Date: Wed, 18 Feb 2026 12:02:48 +0800 Subject: [PATCH] fix: Handle missing Civitai API response fields gracefully Fix KeyError when 'hashes', 'name', or 'model' fields are missing from Civitai API responses. Use .get() with defaults instead of direct dict access in: - LoraMetadata.from_civitai_info() - CheckpointMetadata.from_civitai_info() - EmbeddingMetadata.from_civitai_info() - RecipeScanner._get_hash_from_civitai() - DownloadManager._process_download() Fixes #820 --- py/services/download_manager.py | 4 +- py/services/recipe_scanner.py | 5 ++- py/utils/models.py | 66 ++++++++++++++++----------------- 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/py/services/download_manager.py b/py/services/download_manager.py index 0f090785..83108371 100644 --- a/py/services/download_manager.py +++ b/py/services/download_manager.py @@ -496,7 +496,9 @@ class DownloadManager: return {"success": False, "error": "No mirror URL found"} # 3. Prepare download - file_name = file_info["name"] + file_name = file_info.get("name", "") + if not file_name: + return {"success": False, "error": "No filename found in file info"} save_path = os.path.join(save_dir, file_name) # 5. Prepare metadata based on model type diff --git a/py/services/recipe_scanner.py b/py/services/recipe_scanner.py index 7a10d030..4a5c01eb 100644 --- a/py/services/recipe_scanner.py +++ b/py/services/recipe_scanner.py @@ -1351,8 +1351,9 @@ class RecipeScanner: # Get hash from the first file for file_info in version_info.get('files', []): - if file_info.get('hashes', {}).get('SHA256'): - return file_info['hashes']['SHA256'], False # Return hash with False for isDeleted flag + sha256_hash = (file_info.get('hashes') or {}).get('SHA256') + if sha256_hash: + return sha256_hash, False # Return hash with False for isDeleted flag logger.debug(f"No SHA256 hash found in version info for ID: {model_version_id}") return None, False diff --git a/py/utils/models.py b/py/utils/models.py index af6b91ad..eec140db 100644 --- a/py/utils/models.py +++ b/py/utils/models.py @@ -143,27 +143,27 @@ class LoraMetadata(BaseModelMetadata): @classmethod def from_civitai_info(cls, version_info: Dict, file_info: Dict, save_path: str) -> 'LoraMetadata': """Create LoraMetadata instance from Civitai version info""" - file_name = file_info['name'] + file_name = file_info.get('name', '') base_model = determine_base_model(version_info.get('baseModel', '')) - + # Extract tags and description if available tags = [] description = "" - if 'model' in version_info: - if 'tags' in version_info['model']: - tags = version_info['model']['tags'] - if 'description' in version_info['model']: - description = version_info['model']['description'] - + model_data = version_info.get('model') or {} + if 'tags' in model_data: + tags = model_data['tags'] + if 'description' in model_data: + description = model_data['description'] + return cls( file_name=os.path.splitext(file_name)[0], - model_name=version_info.get('model').get('name', os.path.splitext(file_name)[0]), + model_name=model_data.get('name', os.path.splitext(file_name)[0]), file_path=save_path.replace(os.sep, '/'), size=file_info.get('sizeKB', 0) * 1024, modified=datetime.now().timestamp(), - sha256=file_info['hashes'].get('SHA256', '').lower(), + sha256=(file_info.get('hashes') or {}).get('SHA256', '').lower(), base_model=base_model, - preview_url=None, # Will be updated after preview download + preview_url='', # Will be updated after preview download preview_nsfw_level=0, # Will be updated after preview download from_civitai=True, civitai=version_info, @@ -179,28 +179,28 @@ class CheckpointMetadata(BaseModelMetadata): @classmethod def from_civitai_info(cls, version_info: Dict, file_info: Dict, save_path: str) -> 'CheckpointMetadata': """Create CheckpointMetadata instance from Civitai version info""" - file_name = file_info['name'] + file_name = file_info.get('name', '') base_model = determine_base_model(version_info.get('baseModel', '')) sub_type = version_info.get('type', 'checkpoint') - + # Extract tags and description if available tags = [] description = "" - if 'model' in version_info: - if 'tags' in version_info['model']: - tags = version_info['model']['tags'] - if 'description' in version_info['model']: - description = version_info['model']['description'] - + model_data = version_info.get('model') or {} + if 'tags' in model_data: + tags = model_data['tags'] + if 'description' in model_data: + description = model_data['description'] + return cls( file_name=os.path.splitext(file_name)[0], - model_name=version_info.get('model').get('name', os.path.splitext(file_name)[0]), + model_name=model_data.get('name', os.path.splitext(file_name)[0]), file_path=save_path.replace(os.sep, '/'), size=file_info.get('sizeKB', 0) * 1024, modified=datetime.now().timestamp(), - sha256=file_info['hashes'].get('SHA256', '').lower(), + sha256=(file_info.get('hashes') or {}).get('SHA256', '').lower(), base_model=base_model, - preview_url=None, # Will be updated after preview download + preview_url='', # Will be updated after preview download preview_nsfw_level=0, from_civitai=True, civitai=version_info, @@ -217,28 +217,28 @@ class EmbeddingMetadata(BaseModelMetadata): @classmethod def from_civitai_info(cls, version_info: Dict, file_info: Dict, save_path: str) -> 'EmbeddingMetadata': """Create EmbeddingMetadata instance from Civitai version info""" - file_name = file_info['name'] + file_name = file_info.get('name', '') base_model = determine_base_model(version_info.get('baseModel', '')) sub_type = version_info.get('type', 'embedding') - + # Extract tags and description if available tags = [] description = "" - if 'model' in version_info: - if 'tags' in version_info['model']: - tags = version_info['model']['tags'] - if 'description' in version_info['model']: - description = version_info['model']['description'] - + model_data = version_info.get('model') or {} + if 'tags' in model_data: + tags = model_data['tags'] + if 'description' in model_data: + description = model_data['description'] + return cls( file_name=os.path.splitext(file_name)[0], - model_name=version_info.get('model').get('name', os.path.splitext(file_name)[0]), + model_name=model_data.get('name', os.path.splitext(file_name)[0]), file_path=save_path.replace(os.sep, '/'), size=file_info.get('sizeKB', 0) * 1024, modified=datetime.now().timestamp(), - sha256=file_info['hashes'].get('SHA256', '').lower(), + sha256=(file_info.get('hashes') or {}).get('SHA256', '').lower(), base_model=base_model, - preview_url=None, # Will be updated after preview download + preview_url='', # Will be updated after preview download preview_nsfw_level=0, from_civitai=True, civitai=version_info,