HumanSignal/label-studio · error · RuntimeError
Export storage {self.id}: {failed} annotation(s) failed to e
Error message
Export storage {self.id}: {failed} annotation(s) failed to export What it means
save_annotations re-raises a RuntimeError when any annotation failed to export to the target storage in its worker thread. A full sync (update_status=True) marks the storage FAILED via on_failure; partial exports (update_status=False) leave the shared status untouched but still propagate the error to the caller/RQ job.
Source
Thrown at label_studio/io_storages/base_models.py:983
# Resolve the future so exceptions raised inside save_annotation worker threads
# are not silently swallowed. Best-effort: keep exporting the rest of the batch.
try:
future.result()
except Exception:
failed += 1
logger.error(f'Export storage {self.id}: failed to export annotation', exc_info=True)
continue
annotation_exported += 1
if update_status:
self.info_update_progress(
last_sync_count=annotation_exported, total_annotations=total_annotations
)
# Surface worker-thread failures by re-raising so the RQ job is marked failed. Full syncs set the
# storage FAILED via on_failure=storage_background_failure; partial exports (update_status=False)
# leave the shared storage status untouched, which is the intended contract.
if failed:
raise RuntimeError(f'Export storage {self.id}: {failed} annotation(s) failed to export')
if update_status:
self.info_set_completed(last_sync_count=annotation_exported, total_annotations=total_annotations)
def save_all_annotations(self):
self.save_annotations(Annotation.objects.filter(project=self.project))
def save_only_new_annotations(self):
"""Do not update existing annotations, only ensure that all annotations have an ExportStorageLink"""
# Get the storage-specific ExportStorageLink model
storage_link_model = self.links.model
new_annotations = Annotation.objects.filter(project=self.project).exclude(
id__in=storage_link_model.objects.filter(storage=self, annotation__project=self.project).values(
'annotation_id'
)
)
self.save_annotations(new_annotations)
View on GitHub (pinned to 0b49e9b539)
Solutions
- Read the preceding per-annotation error logs (logger.exception in the worker) to find the root cause (permissions, missing bucket, network)
- Re-verify storage credentials with 'Validate and Import' / instance.validate_connection() and fix them in storage settings
- Retrying the sync (save_all_annotations) after fixing; failed annotations are retried since only successful ones were saved
- If partial export is intentional, wrap the call in try/except RuntimeError and treat failure count as informational
Example fix
// before
export_storage.save_only_new_annotations() # RuntimeError aborts caller
// after
try:
export_storage.save_only_new_annotations()
except RuntimeError as e:
logger.warning('Partial export failure: %s', e) # or fix creds and resync Defensive patterns
Strategy: try-catch
Validate before calling
def can_export(storage, annotation_ids):
try:
storage.validate_connection()
return True
except Exception as e:
logger.error('Export storage %s unreachable: %s', storage.id, e)
return False Try / catch
try:
storage.save_all_annotations()
except RuntimeError as e:
logger.error('Export sync failed: %s', e)
# inspect per-annotation worker logs, fix storage, then resync
storage.sync_status = 'failed' Prevention
- Run validate_connection() before long export jobs
- Rotate-test cloud credentials before scheduled syncs
- Monitor the server log for per-annotation failures during sync
- Wrap partial exports (update_status=False) in explicit error handling
When it happens
Trigger: Calling save_all_annotations/save_only_new_annotations on an export storage (GCS/S3/Azure) whose target bucket credentials are invalid, bucket/prefix missing, or network unreachable, so some per-annotation uploads fail while others succeed.
Common situations: Rotated/revoked cloud credentials mid-run; bucket deleted or renamed; write permissions removed after storage was created; transient network outages during large export syncs; partial export tests expecting full success.
Related errors
- File "{key}" is not a JSON/JSONL/Parquet file. Only .json, .
- Error validating storage connection
- You do not have permission to create storages for this proje
- Failed to list storage files
- "form_layout.yml" is not found for {self.__class__.__name__}
AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29).
Data as JSON: /api/errors/f201362104a546d7.
Report an issue: GitHub.