[ecovacs] Catch more exceptions when parsing vacuum data#20016
[ecovacs] Catch more exceptions when parsing vacuum data#20016lsiepel merged 1 commit intoopenhab:mainfrom
Conversation
Missing fields in the responses so far went uncaught: 2026-01-08 14:19:31.852 [WARN ] [mmon.WrappedScheduledExecutorService] - Scheduled runnable ended with an exception: java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "resp.waterAmount" is null at org.openhab.binding.ecovacs.internal.api.commands.GetMoppingWaterAmountCommand.convertResponse(GetMoppingWaterAmountCommand.java:45) ~[?:?] at org.openhab.binding.ecovacs.internal.api.commands.GetMoppingWaterAmountCommand.convertResponse(GetMoppingWaterAmountCommand.java:1) ~[?:?] at org.openhab.binding.ecovacs.internal.api.impl.EcovacsApiImpl.sendIotCommand(EcovacsApiImpl.java:308) ~[?:?] at org.openhab.binding.ecovacs.internal.api.impl.EcovacsIotMqDevice.sendCommand(EcovacsIotMqDevice.java:98) ~[?:?] at org.openhab.binding.ecovacs.internal.handler.EcovacsVacuumHandler.lambda$15(EcovacsVacuumHandler.java:632) ~[?:?] at org.openhab.binding.ecovacs.internal.handler.EcovacsVacuumHandler.doWithDevice(EcovacsVacuumHandler.java:822) ~[?:?] at org.openhab.binding.ecovacs.internal.handler.EcovacsVacuumHandler.pollData(EcovacsVacuumHandler.java:591) ~[?:?] at org.openhab.binding.ecovacs.internal.api.util.SchedulerTask.run(SchedulerTask.java:95) ~[?:?] at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) ~[?:?] at java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[?:?] at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[?:?] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[?:?] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[?:?] at java.lang.Thread.run(Thread.java:1583) [?:?] Make sure to catch those and make the user aware of it by setting the thing to offline / communication error instead of just emitting a warning in the log, so they get a chance to report that problem. With the previous behavior everything apparently was OK, but channels were not populated correctly. Signed-off-by: Danny Baumann <dannybaumann@web.de>
|
This looks like it can be fixed by a null check on Edit: I would also not try to catch the |
There was a problem hiding this comment.
Pull request overview
This PR improves error handling in the Ecovacs vacuum binding by catching more exception types when parsing vacuum data. The change ensures that unexpected runtime exceptions (like NullPointerExceptions from missing fields in API responses) are properly caught and reported to users by setting the thing offline, rather than just logging warnings while leaving the thing in an apparently OK state with unpopulated channels.
Changes:
- Broadened exception catching from specific exceptions to general
Exceptionin three device communication classes - Removed now-unused
DataParsingExceptionimports from two files
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| EcovacsXmppDevice.java | Changed catch clause to catch all exceptions instead of just specific parsing exceptions |
| EcovacsIotMqDevice.java | Changed catch clause to catch all exceptions and removed unused DataParsingException import |
| EcovacsApiImpl.java | Changed catch clause to catch all exceptions and removed unused DataParsingException import |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| } | ||
| } catch (DataParsingException | ParserConfigurationException | TransformerException e) { | ||
| } catch (Exception e) { |
There was a problem hiding this comment.
Catching generic Exception is overly broad and may mask unexpected errors that shouldn't be recoverable (like OutOfMemoryError or other serious JVM errors). Consider catching RuntimeException instead, or at minimum catching specific exceptions like NullPointerException, IllegalStateException, and keeping the existing specific exceptions. This allows truly exceptional errors to propagate properly while still catching the parsing and data-related runtime exceptions mentioned in the PR description.
| } catch (Exception e) { | |
| } catch (RuntimeException e) { |
| logger.trace("{}: Got MQTT message on topic {}: {}", getSerialNumber(), receivedTopic, payload); | ||
| parser.handleMessage(eventName, payload); | ||
| } catch (DataParsingException e) { | ||
| } catch (Exception e) { |
There was a problem hiding this comment.
Catching generic Exception is overly broad and may mask unexpected errors that shouldn't be recoverable (like OutOfMemoryError or other serious JVM errors). Consider catching RuntimeException instead, or at minimum catching specific exceptions like NullPointerException, IllegalStateException, and keeping the existing specific exceptions. This allows truly exceptional errors to propagate properly while still catching the parsing and data-related runtime exceptions mentioned in the PR description.
| } catch (Exception e) { | |
| } catch (RuntimeException e) { |
| try { | ||
| return command.convertResponse(commandResponse, desc.protoVersion, gson); | ||
| } catch (DataParsingException e) { | ||
| } catch (Exception e) { |
There was a problem hiding this comment.
Catching generic Exception is overly broad and may mask unexpected errors that shouldn't be recoverable (like OutOfMemoryError or other serious JVM errors). Consider catching RuntimeException instead, or at minimum catching specific exceptions like NullPointerException, IllegalStateException, and keeping the existing specific exceptions. This allows truly exceptional errors to propagate properly while still catching the parsing and data-related runtime exceptions mentioned in the PR description.
| } catch (Exception e) { | |
| } catch (RuntimeException e) { |
|
Gentle ping @maniac103, you seem busy at the moment with android changes, maybe you can have a look at the comments afterwards. |
Thanks for the ping, I completely forgot about this.
Those points together mean that an uncaught exception in that runnable is 'catastrophic' in that device data update will break silently without recovering. It's the intention of this PR to change this, and to do that, I need to avoid uncaught exceptions as much as possible - why is why I'm catching all of them, including unexpected ones. They'll still be logged (via Unlike stated in the Copilot comments,
As mentioned, the root cause for this was already fixed separately (#20017). |
lsiepel
left a comment
There was a problem hiding this comment.
Thanks for the quick reply. LGTM
Missing fields in the responses so far went uncaught:
Make sure to catch those and make the user aware of it by setting the thing to offline / communication error instead of just emitting a warning in the log, so they get a chance to report that problem. With the previous behavior everything apparently was OK, but channels were not populated correctly.
(The root cause for the particular stack trace above was a wrong feature flag, which will be fixed in a separate MR)