{"record":{"id":"c0b65203d9ce1526","repo":"HumanSignal/label-studio","slug":"response-is-not-list-payload","errorCode":null,"errorMessage":"Response is not list: {payload}","messagePattern":"Response is not list: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"label_studio/io_storages/all_api.py","lineNumber":144,"sourceCode":"        extensions={\n            'x-fern-sdk-group-name': ['import_storage'],\n            'x-fern-sdk-method-name': 'list',\n            'x-fern-audiences': ['internal'],\n        },\n    ),\n)\nclass AllImportStorageListAPI(generics.ListAPIView):\n    queryset = S3ImportStorage.objects.none()\n    parser_classes = (JSONParser, FormParser, MultiPartParser)\n    permission_required = all_permissions.storages_view\n\n    def _get_response(self, api, request, *args, **kwargs):\n        try:\n            view = api.as_view()\n            response = view(request._request, *args, **kwargs)\n            payload = response.data\n            if not isinstance(payload, list):\n                raise ValueError(f'Response is not list: {payload}')\n            return payload\n        except Exception:\n            logger.error(f\"Can't process {api.__class__.__name__}\", exc_info=True)\n            return []\n\n    def list(self, request, *args, **kwargs):\n        list_responses = sum(\n            [self._get_response(s['import_list_api'], request, *args, **kwargs) for s in _common_storage_list], []\n        )\n        return Response(list_responses)\n\n\n@method_decorator(\n    name='get',\n    decorator=extend_schema(\n        tags=['Storage'],\n        summary='List all export storages from the project',\n        description='Retrieve a list of the export storages of all types with their IDs.',","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/io_storages/all_api.py#L126-L162","documentation":"AllImportStorageListAPI._get_response proxies a per-storage-type list view and expects response.data to be a list. If the DRF view returns a non-list payload (object, dict, paginated response), it raises ValueError — but the method catches all exceptions, logs 'Can't process ...', and returns [] so that the aggregated sum() of lists still works.","triggerScenarios":"A storage type's import_list_api view returning a paginated/serialized object payload instead of a plain list, or raising before producing list data — typically when one storage backend is misconfigured.","commonSituations":"Adding a new storage type whose list API returns a dict; pagination enabled on the inner view changing response shape; credentials misconfigured for S3/GCS/Azure causing the inner view to error and fall into the catch-all.","solutions":["Check the error log for \"Can't process <ApiName>\" to identify which storage API failed.","Ensure the storage type's list API returns a plain list (disable pagination or return .data list).","Fix the misconfigured storage backend (credentials/bucket) causing the inner view to fail.","Optionally make _get_response unwrap paginated objects ({'results': [...]}) before the isinstance check."],"exampleFix":"// before\npayload = response.data\nif not isinstance(payload, list):\n    raise ValueError(f'Response is not list: {payload}')\n// after\npayload = response.data\nif isinstance(payload, dict) and 'results' in payload:\n    payload = payload['results']\nif not isinstance(payload, list):\n    raise ValueError(f'Response is not list: {payload}')","handlingStrategy":"type-guard","validationCode":"resp = view(request._request, *args, **kwargs)\npayload = resp.data.get('results') if isinstance(resp.data, dict) else resp.data\nif not isinstance(payload, list):\n    logger.warning('import storage list for %s returned non-list', api.__name__)","typeGuard":"def is_list_payload(resp) -> bool:\n    data = resp.data.get('results') if isinstance(resp.data, dict) else resp.data\n    return isinstance(data, list)","tryCatchPattern":"try:\n    payload = self._get_response(api, request, *args, **kwargs)\nexcept ValueError:\n    payload = []  # per-type isolation: one bad backend shouldn't break the aggregate","preventionTips":["Keep inner storage list views pagination-free or unwrap 'results'.","Log which storage API failed (already done) and alert on repeated occurrences.","Add a contract test asserting each storage list API returns a JSON array."],"tags":["api","storages","response-shape"],"backgroundTag":"unexpected-response-shape","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}