{"record":{"id":"f201362104a546d7","repo":"HumanSignal/label-studio","slug":"export-storage-self-id-failed-annotation-s-f","errorCode":null,"errorMessage":"Export storage {self.id}: {failed} annotation(s) failed to export","messagePattern":"Export storage (.+?): (.+?) annotation\\(s\\) failed to export","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"label_studio/io_storages/base_models.py","lineNumber":983,"sourceCode":"                    # Resolve the future so exceptions raised inside save_annotation worker threads\n                    # are not silently swallowed. Best-effort: keep exporting the rest of the batch.\n                    try:\n                        future.result()\n                    except Exception:\n                        failed += 1\n                        logger.error(f'Export storage {self.id}: failed to export annotation', exc_info=True)\n                        continue\n                    annotation_exported += 1\n                    if update_status:\n                        self.info_update_progress(\n                            last_sync_count=annotation_exported, total_annotations=total_annotations\n                        )\n\n        # Surface worker-thread failures by re-raising so the RQ job is marked failed. Full syncs set the\n        # storage FAILED via on_failure=storage_background_failure; partial exports (update_status=False)\n        # leave the shared storage status untouched, which is the intended contract.\n        if failed:\n            raise RuntimeError(f'Export storage {self.id}: {failed} annotation(s) failed to export')\n\n        if update_status:\n            self.info_set_completed(last_sync_count=annotation_exported, total_annotations=total_annotations)\n\n    def save_all_annotations(self):\n        self.save_annotations(Annotation.objects.filter(project=self.project))\n\n    def save_only_new_annotations(self):\n        \"\"\"Do not update existing annotations, only ensure that all annotations have an ExportStorageLink\"\"\"\n        # Get the storage-specific ExportStorageLink model\n        storage_link_model = self.links.model\n        new_annotations = Annotation.objects.filter(project=self.project).exclude(\n            id__in=storage_link_model.objects.filter(storage=self, annotation__project=self.project).values(\n                'annotation_id'\n            )\n        )\n        self.save_annotations(new_annotations)\n","sourceCodeStart":965,"sourceCodeEnd":1001,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/io_storages/base_models.py#L965-L1001","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nexport_storage.save_only_new_annotations()  # RuntimeError aborts caller\n// after\ntry:\n    export_storage.save_only_new_annotations()\nexcept RuntimeError as e:\n    logger.warning('Partial export failure: %s', e)  # or fix creds and resync","handlingStrategy":"try-catch","validationCode":"def can_export(storage, annotation_ids):\n    try:\n        storage.validate_connection()\n        return True\n    except Exception as e:\n        logger.error('Export storage %s unreachable: %s', storage.id, e)\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    storage.save_all_annotations()\nexcept RuntimeError as e:\n    logger.error('Export sync failed: %s', e)\n    # inspect per-annotation worker logs, fix storage, then resync\n    storage.sync_status = 'failed'","preventionTips":["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"],"tags":["storage","export","sync","cloud"],"backgroundTag":"partial-export-failure","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}