{"record":{"id":"ec399bbe46c0f87e","repo":"HumanSignal/label-studio","slug":"export-type-format-is-not-converted-yet","errorCode":null,"errorMessage":"{export_type} format is not converted yet","messagePattern":"(.+?) format is not converted yet","errorType":"http","errorClass":"NotFound","httpStatus":404,"severity":"error","filePath":"label_studio/data_export/api.py","lineNumber":560,"sourceCode":"        return project\n\n    def get_queryset(self):\n        project = self._get_project()\n        return super().get_queryset().filter(project=project)\n\n    def get(self, request, *args, **kwargs):\n        snapshot = self.get_object()\n        export_type = request.GET.get('exportType')\n\n        if snapshot.status != Export.Status.COMPLETED:\n            return HttpResponse('Export is not completed', status=404)\n\n        if flag_set('fflag_fix_all_lsdv_4813_async_export_conversion_22032023_short', request.user):\n            file = snapshot.file\n            if export_type is not None and export_type != 'JSON':\n                converted_file = snapshot.converted_formats.filter(export_type=export_type).first()\n                if converted_file is None:\n                    raise NotFound(f'{export_type} format is not converted yet')\n                file = converted_file.file\n\n            if isinstance(file.storage, FileSystemStorage):\n                url = file.storage.url(file.name)\n            else:\n                url = file.storage.url(file.name, storage_url=True)\n            protocol = urlparse(url).scheme\n            download_name = snapshot.get_download_filename(file.name)\n\n            # NGINX downloads are a solid way to make uwsgi workers free\n            if settings.USE_NGINX_FOR_EXPORT_DOWNLOADS:\n                # let NGINX handle it\n                response = HttpResponse()\n                # below header tells NGINX to catch it and serve, see docker-config/nginx-app.conf\n                redirect = '/file_download/' + protocol + '/' + url.replace(protocol + '://', '')\n                response['X-Accel-Redirect'] = redirect\n                response['Content-Disposition'] = f'attachment; filename=\"{download_name}\"'\n                response['filename'] = download_name","sourceCodeStart":542,"sourceCodeEnd":578,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/data_export/api.py#L542-L578","documentation":"When async export conversion is enabled (fflag_fix_all_lsdv_4813...), the export download endpoint get() serves the file converted to the requested export_type. If no ConvertedFormat record with that export_type exists on the export snapshot (conversion never completed), it raises NotFound with this message. Only 'JSON' is available without conversion.","triggerScenarios":"Requesting GET /api/projects/<id>/exports/<snapshot_id>?export_type=CSV (or XLSX etc.) while async conversion is enabled, before the conversion job for that format has produced a ConvertedFormat row.","commonSituations":"Client polls for a converted format immediately after snapshot creation before the async job finished; a previous conversion failed so no record exists; requesting a format never queued; feature flag recently enabled so legacy snapshots lack converted formats.","solutions":["First POST to the conversion endpoint for that export_type, wait for completion (status becomes completed), then GET the file.","Poll the ConvertedFormat status via the API before attempting download.","Re-trigger conversion if a prior job failed (FAILED records are retriable via the convert endpoint).","Use export_type=JSON, which is always available from the snapshot without conversion."],"exampleFix":"// before\nGET /api/projects/1/exports/abc?export_type=CSV  -> 404 not converted yet\n\n// after\nPOST /api/projects/1/exports/abc/convert  (export_type=CSV)\n# wait until completed\nGET /api/projects/1/exports/abc?export_type=CSV","handlingStrategy":"retry","validationCode":"def can_download(snapshot, export_type):\n    if export_type in (None, 'JSON'):\n        return True\n    return snapshot.converted_formats.filter(\n        export_type=export_type,\n        status='completed'\n    ).exists()","typeGuard":null,"tryCatchPattern":"try:\n    file = download_export(snapshot_id, export_type='CSV')\nexcept NotFound:\n    trigger_conversion(snapshot_id, 'CSV')\n    wait_for_conversion(snapshot_id, 'CSV')  # poll ConvertedFormat status\n    file = download_export(snapshot_id, export_type='CSV')","preventionTips":["Always POST the convert job and wait for completion before GET for non-JSON formats","Poll ConvertedFormat status instead of immediately downloading","Remember legacy snapshots created before the async flag lack converted formats"],"tags":["data-export","async-job","http-404","race-condition"],"backgroundTag":"export-format-not-ready","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}