Symptom

GPIO output pin state events (PIN_CHANGED) were emitted to SSE subscribers even when the underlying gRPC call to the krill-pi4j daemon threw an exception — clients believed the pin changed state when the hardware never moved. Similarly, a SerialDevice WRITE that failed (sendCommand() returning false) did not propagate the failure to nodeManager.failed(); the node settled to NONE instead of FAILED, hiding the error from operators.

Root cause

In ServerPiManager.setHigh() and setLow(), the scope.launch { EventFlowContainer.postEvent(...) } call was placed after the try/catch, not inside the try block. The exception was caught and logged, but execution continued and posted the event unconditionally. Any hardware failure (gRPC error, channel unavailable) silently told clients the pin changed state.

In ServerSerialDeviceManager.post() (WRITE path), sendCommand() returning false was handled only with logger.w. Execution fell through to resetWatchdog() and nodeManager.setStateToNone(), making the failed write look like a successful idle state to the node system and to MCP clients.

Fix

Prevention