{"record":{"id":"79780584696e48f9","repo":"HumanSignal/label-studio","slug":"form-layout-yml-is-not-found-for-self-class","errorCode":null,"errorMessage":"\"form_layout.yml\" is not found for {self.__class__.__name__}","messagePattern":"\"form_layout\\.yml\" is not found for (.+?)","errorType":"http","errorClass":"NotFound","httpStatus":404,"severity":"warning","filePath":"label_studio/io_storages/api.py","lineNumber":244,"sourceCode":"                    break\n\n            return Response({'files': files})\n        except Exception as exc:\n            logger.exception('Error listing storage files: %s', exc)\n            raise ValidationError('Failed to list storage files')\n\n\n@extend_schema(exclude=True)\nclass StorageFormLayoutAPI(generics.RetrieveAPIView):\n    permission_required = all_permissions.storages_change\n    parser_classes = (JSONParser, FormParser, MultiPartParser)\n    storage_type = None\n\n    @extend_schema(exclude=True)\n    def get(self, request, *args, **kwargs):\n        form_layout_file = os.path.join(os.path.dirname(inspect.getfile(self.__class__)), 'form_layout.yml')\n        if not os.path.exists(form_layout_file):\n            raise NotFound(f'\"form_layout.yml\" is not found for {self.__class__.__name__}')\n\n        form_layout = read_yaml(form_layout_file)\n        form_layout = self.post_process_form(form_layout)\n        return Response(form_layout[self.storage_type])\n\n    def post_process_form(self, form_layout):\n        return form_layout\n\n\nclass ImportStorageValidateAPI(StorageValidateAPI):\n    serializer_class = ImportStorageSerializer\n\n\nclass ExportStorageValidateAPI(StorageValidateAPI):\n    serializer_class = ExportStorageSerializer\n\n\nclass ImportStorageFormLayoutAPI(StorageFormLayoutAPI):","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/io_storages/api.py#L226-L262","documentation":"StorageFormLayoutAPI.get locates a form_layout.yml next to the storage class's module file; if the YAML file does not exist for that storage backend, it raises NotFound naming the class. The endpoint serves the dynamic form definition used by the UI to render storage connection forms.","triggerScenarios":"GET /api/storages/form-layouts?storage_type=... for a storage backend whose package directory lacks form_layout.yml (custom or third-party storage type without the file, or source distribution missing the data file).","commonSituations":"Custom storage plugin copied from a template without adding form_layout.yml; pip package built without including the YAML (missing package_data in setup.py); pointing storage_type at a backend that ships no form layout.","solutions":["Add a form_layout.yml to the storage backend package directory","If packaging a custom storage, include YAML files in package_data/MANIFEST.in","Verify storage_type matches a supported backend that ships form_layout.yml","Reinstall the label-storage package from a complete source checkout"],"exampleFix":"// before\nmy_storages/ftp/  # contains models.py, serializers.py but no form_layout.yml → 404\n// after\nmy_storages/ftp/form_layout.yml  # added, listing import/export form fields → 200","handlingStrategy":"fallback","validationCode":"# for custom storage packages, ensure the file ships before registering the endpoint\nimport inspect, os\nmod_dir = os.path.dirname(inspect.getfile(MyStorageAPI.serializer_class.Meta.model))\nassert os.path.exists(os.path.join(mod_dir, \"form_layout.yml\")), \"form_layout.yml missing\"","typeGuard":null,"tryCatchPattern":"try:\n    resp = requests.get(f\"{LS_URL}/api/storages/form-layouts/\", params={\"type\": storage_type}, headers=headers)\n    resp.raise_for_status()\nexcept requests.HTTPError as e:\n    if resp.status_code == 404:\n        layout = DEFAULT_FORM_LAYOUT  # fall back to a locally-defined schema","preventionTips":["Ship form_layout.yml in custom storage packages","Add YAML files to package_data/MANIFEST.in so wheels include them","Test the form-layout endpoint in CI for every storage backend"],"tags":["configuration","missing-file","yaml","ui","storage"],"backgroundTag":"missing-resource-file","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}