{"record":{"id":"5af66e405d2e7473","repo":"invoke-ai/InvokeAI","slug":"attempt-to-start-the-download-service-twice","errorCode":null,"errorMessage":"Attempt to start the download service twice","messagePattern":"Attempt to start the download service twice","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"invokeai/app/services/download/download_default.py","lineNumber":103,"sourceCode":"            # download_proxy must still be honored rather than silently ignored when both\n            # settings are present. It is applied per request (see the single call site in\n            # _do_download) because that is the only level that takes precedence over\n            # ambient *_PROXY variables in a plain Session.\n            self._requests = requests.Session()\n            if self._app_config.download_proxy:\n                proxy = self._app_config.download_proxy\n                self._request_proxies = {\"http\": proxy, \"https\": proxy}\n        else:\n            self._requests = build_guarded_session(proxy=self._app_config.download_proxy)\n            warn_if_proxied(self._requests, self._logger)\n        self._accept_download_requests = False\n        self._max_parallel_dl = max_parallel_dl\n\n    def start(self, *args: Any, **kwargs: Any) -> None:\n        \"\"\"Start the download worker threads.\"\"\"\n        with self._lock:\n            if self._worker_pool:\n                raise Exception(\"Attempt to start the download service twice\")\n            self._stop_event.clear()\n            self._start_workers(self._max_parallel_dl)\n            self._accept_download_requests = True\n\n    def stop(self, *args: Any, **kwargs: Any) -> None:\n        \"\"\"Stop the download worker threads.\"\"\"\n        with self._lock:\n            if not self._worker_pool:\n                return\n            self._accept_download_requests = False  # reject attempts to add new jobs to queue\n            queued_jobs = [x for x in self.list_jobs() if x.status == DownloadJobStatus.WAITING]\n            active_jobs = [x for x in self.list_jobs() if x.status == DownloadJobStatus.RUNNING]\n            if queued_jobs:\n                self._logger.warning(f\"Cancelling {len(queued_jobs)} queued downloads\")\n            if active_jobs:\n                self._logger.info(f\"Waiting for {len(active_jobs)} active download jobs to complete\")\n            with self._queue.mutex:\n                self._queue.queue.clear()","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/download/download_default.py#L85-L121","documentation":"DownloadService.start() is idempotency-guarded: under a lock it checks _worker_pool and raises if workers already exist. Calling start() twice without an intervening stop() would duplicate worker threads, so it is rejected.","triggerScenarios":"Calling service.start() a second time on an already-started DownloadService instance (e.g. app startup code invoked again, re-import/re-initialization paths, or a hot-reload that re-runs init).","commonSituations":"Application double-initialization during tests or lifespan handlers, calling start() after a restart script without stop(), or constructing two references to the same service and starting both.","solutions":["Guard with `if not service._worker_pool: service.start()` or track your own started flag before calling start()","Call stop() before start() if a restart is intended","Ensure only one code path owns service lifecycle (single init point in app startup)"],"exampleFix":"// before\nservice.start()\nservice.start()  # raises\n// after\n\nif not getattr(service, \"_worker_pool\", None):\n    service.start()","handlingStrategy":"try-catch","validationCode":"def download_service_started(s) -> bool:\n    return getattr(s, \"_worker_pool\", None) is not None","typeGuard":"def can_start(s: object) -> bool:\n    return hasattr(s, \"start\") and getattr(s, \"_worker_pool\", None) is None","tryCatchPattern":"try:\n    service.start()\nexcept Exception as e:\n    if \"twice\" in str(e):\n        logger.warning(\"download service already started; ignoring\")\n    else:\n        raise","preventionTips":["Start the service in exactly one initialization path (app lifespan)","Track started state in a wrapper/flag instead of calling start() repeatedly","In tests, use a fixture that starts the service once per session","Pair every stop() with a clear restart procedure"],"tags":["lifecycle","double-start","download","threads"],"backgroundTag":"service-already-started","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}