{"record":{"id":"0c8b374d28e14930","repo":"ruvnet/RuView","slug":"hardware-service-is-not-running","errorCode":null,"errorMessage":"Hardware service is not running","messagePattern":"Hardware service is not running","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"archive/v1/src/services/hardware_service.py","lineNumber":419,"sourceCode":"        \"\"\"Reset service state.\"\"\"\n        self.stats = {\n            \"total_samples\": 0,\n            \"successful_samples\": 0,\n            \"failed_samples\": 0,\n            \"average_sample_rate\": 0.0,\n            \"last_sample_time\": None,\n            \"connected_routers\": len(self.router_interfaces)\n        }\n        \n        self.recent_samples.clear()\n        self.last_error = None\n        \n        self.logger.info(\"Hardware service reset\")\n    \n    async def trigger_manual_collection(self, router_id: Optional[str] = None) -> Dict[str, Any]:\n        \"\"\"Manually trigger data collection.\"\"\"\n        if not self.is_running:\n            raise RuntimeError(\"Hardware service is not running\")\n        \n        results = {}\n        \n        if router_id:\n            # Collect from specific router\n            if router_id not in self.router_interfaces:\n                raise ValueError(f\"Router {router_id} not found\")\n            \n            interface = self.router_interfaces[router_id]\n            try:\n                csi_data = await interface.get_csi_data()\n                if csi_data is not None:\n                    await self._process_collected_data(router_id, csi_data)\n                    results[router_id] = {\"success\": True, \"data_shape\": csi_data.shape if hasattr(csi_data, 'shape') else None}\n                else:\n                    results[router_id] = {\"success\": False, \"error\": \"No data received\"}\n            except Exception as e:\n                results[router_id] = {\"success\": False, \"error\": str(e)}","sourceCodeStart":401,"sourceCodeEnd":437,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/archive/v1/src/services/hardware_service.py#L401-L437","documentation":"trigger_manual_collection() refuses to run when the service's is_running flag is False, raising RuntimeError. The flag is set by start() and cleared by stop()/shutdown, so this error means the manual-collection API was called on a service that was never started or has already been stopped (for example after reset() or an error path). It is a lifecycle-ordering error, not a hardware failure.","triggerScenarios":"Calling trigger_manual_collection() before await start(); calling it after stop() or shutdown(); calling after reset() cleared state without a subsequent start; background loop crashed and flipped the running state off.","commonSituations":"FastAPI/health endpoints that trigger collection on demand while the service lifecycle is managed elsewhere; test code that forgets the fixture ordering; race between shutdown and an in-flight request.","solutions":["Start the service first: 'await hardware_service.start()' (and initialize() if required) before any manual collection.","Guard call sites with 'if not hardware_service.is_running: await hardware_service.start()'.","Reject manual-collection requests at the API layer with 409/503 when the service is not running, instead of letting RuntimeError escape.","If the service stopped unexpectedly, inspect last_error and restart it."],"exampleFix":"# before\nresult = await hardware_service.trigger_manual_collection()  # RuntimeError\n\n# after\nif not hardware_service.is_running:\n    await hardware_service.start()\nresult = await hardware_service.trigger_manual_collection()","handlingStrategy":"validation","validationCode":"if not hardware_service.is_running:\n    await hardware_service.start()\nresult = await hardware_service.trigger_manual_collection()","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Start the HardwareService during app startup (lifespan hook) so manual collection is always valid afterwards.","Return HTTP 503 from manual-collection endpoints when is_running is False instead of letting RuntimeError escape.","In tests, use fixtures that start the service before any collection call."],"tags":["service","lifecycle","asyncio","hardware"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}