The rule is executed with an error at OpenHAB startup

Hello.

OH: 4.3.0-4.3.5
JAVA: Zulu v17.58.21 CA-JDK v17.0.15 x64
OS: Windows Server 2022 Std Latest
JS: application/javascript ECMAScript 262 Edition 11
Experience: 3+ years with OH v3&4

I have a rule written that sends a notification when OpenHab starts.
Before version 4.0.2 there were no problems.
Now, an error does not always occur.

2025-05-12 14:48:38.399 [ERROR] [.handler.AbstractScriptModuleHandler] - Script execution of rule with UID 'openhab-1' failed: 'sendTelegram' is not a member of 'org.openhab.core.thing.binding.ThingActions'; line 24, column 21, length 48 in openhab
// Telegram
val Long          bot_channel_public = 1L
val Long          bot_channel_private = 2L
val String        bot_answer_private = "@myname"
val String        bot_thing = "telegram:telegramBot:mybot"

// openhab-1
// BUGS: [INFO ] [ab.core.model.script.openhab_startup] - 🏁 OpenHab ready. / 4.0.2+
rule "OpenHAB: Startup"
when
  System reached start level 100
then  
  /* MQTT 
  val _broker = getActions("mqtt", "mqtt:broker:mosquitto");
  if(null !== _broker)  
    _broker.publishMQTT("openhab/status", "online");  
  */

  val _message = "🏁 OpenHab ready.";
  logInfo("openhab_startup", _message);
  
  if(ON !== internet_status.state || ON !== alarm_control_ims_send.state) return;  
  val _bot = getActions("telegram", bot_thing);  
  if(null !== _bot) _bot.sendTelegram(bot_channel_private, _message);
end

// LINE 24:   if(null !== _bot) _bot.sendTelegram(bot_channel_private, _message);

Why does this happen, I seem to be checking for null.
Why is telegram bindning not initialized yet by the time “System reached start level 100”?
What am I doing wrong?

2025-05-12 14:46:09.992 [INFO ] [org.openhab.core.Activator          ] - Starting openHAB 4.3.5 (Release Build)
2025-05-12 14:46:25.557 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'jdbc.persist'
2025-05-12 14:46:33.842 [INFO ] [.core.model.lsp.internal.ModelServer] - Started Language Server Protocol (LSP) service on port 5007
2025-05-12 14:46:34.236 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'openhab.rules'
2025-05-12 14:48:33.397 [INFO ] [e.automation.internal.RuleEngineImpl] - Rule engine started.
2025-05-12 14:48:38.393 [INFO ] [ab.core.model.script.openhab_startup] - 🏁 OpenHab ready.
2025-05-12 14:48:38.399 [ERROR] [.handler.AbstractScriptModuleHandler] - Script execution of rule with UID 'openhab-1' failed: 'sendTelegram' is not a member of 'org.openhab.core.thing.binding.ThingActions'; line 24, column 21, length 48 in openhab
2025-05-12 14:49:04.113 [INFO ] [ab.core.model.script.openhab_startup] - 🏁 OpenHab ready.

I would say the telegram thing is not ready…
If you execute the rule after the start does it work?

Greets

yes, of course it works
And in general it sometimes works at the system startup (depending on my luck)

  1. Why is it not ready?
  2. How to check that it is not ready?
  3. How to wait until it is ready?

I don’t know. I had a similar problem.
so I let the rules load (rename) after a 45 seconds timer after systemstart.
When your rule sometimes work and this is your only problem I would do a little 10 seconds timer at top of your rule…
Greets.

Let’s find out what the respected founding fathers have to say.
I want the right solution (not a hook).

I would try to log out the thing‘s status to check if the thing is maybe still in a state „initializing“. If I remember correctly this state does not prevent OH to reach system level 100.

It takes time for the Telegram binding to log in and authenticate to the service. Beyond that, you’d have to look at debug logs to be sure.

And there is no guarantee that all your Things ever become online. Therefore reaching run level 100 does not depend on all Things becoming ONLINE. Just that they are loaded.

How to check that it is not ready?

In Rules DSL there’s the Thing Status Action. In JS you can get at the whole Thing:

things.getThing("telegram:telegramBot:mybot").status

You can put the call to get and invoke the Thing action into a try/catch. If the catch part gets invoked you’ll know that the Thing isn’t ready.

But what is weird here is that normally you’d get null if the Thing doesn’t exist. I would have expected to get null if the Thing isn’t ready too. But the call to getActions is returning a ThingActions. So it’s getting something back from the Thing. This might be a weirdness in the way the Telegram binding is implemented.

I don’t use this binding so I can only talk in generalities.

AFAIK, you can only reach startlevel 80 when all things are online (except when they are disabled or the bridge is disabled). I know this because I recently fixed a bug related to it.

The thing is, startlevel 80 occurred after rules have been executed (startlevel 40/50).

So if you set your rule to startlevel 100, Things should be online by then.

The last time I looked though, if you have a Thing that never becomes ONLINE (maybe the API is down or something) it gets stuck.

Now that I’ve thought about it a bit though, given that the getActions() is return something other than null makes me think maybe the Thing is ONLINE afterall but there’s something else weird is going on specific to the Telegram binding.

Indeed. What do you think we should do in that situation? I’m thinking while we can wait a bit, we should eventually just time out and advance to the next startlevel? Considering that, it’s pointless to cause the system to get stuck at startlevel 70 (forever), because in comparison, even when you have all Things ONLINE at startup, they could also subsequently go offline afterwards anyway?

Come to think of it, I have many Things that are OFFLINE (from startup) yet my startlevel is at 100, hmm… I need to look into that, just to understand this better.

I don’t have a problem with that behavior. It’s just another data point on this thread to figure out what’s going wrong.

But the implication is that if you really need a rule to run every time OH starts up you need to use a start level < 70. And it’s not uncommon for some bindings to take a long time to get their Things ONLINE and some that are flakey (e.g. Chromecast) where sometimes the Things come online during startup, sometimes do not.

I’d be happy with that behavior too. It does feel weird to only ever get to 100 in a perfect boot. On-the-other-hand, getting to 100 means that all the Things are ONLINE so everything should be operational. If we time out you might end up running a rule and the Thing Actions not work (for example) because the Thing is stuck.

It seems like there is no good answer I think. But I do like the idea of timing out and proceeding anyway to reach 100. Users should do error checking in their rules anyway becuase there’s no guarantee that if the Thing was online a minute ago that it is still online now.

There may have been some recent changes (beyond your PR) that changed the behavior. Like I said, half the time my Chomecast Things don’t come online right away so I’ve never depended on start level 100 for anything.

Thank you! I have carefully studied this conversation, it was very informative, I have understood the architecture of initialization of rules, things, binding.
I have rewritten the code and in the future I will check the status of things before using them, I just did not know that it is possible to check this. Very useful. It is a pity that this is not indicated anywhere in the description. I think it would be great if such situations were explained in the help.

rule "OpenHAB: Startup"
when
  System reached start level 100
then  
  val _message = "🏁 OpenHAB ready.";
  logInfo("openhab_startup", _message);
  
  if(ON !== internet_status.state || ON !== alarm_control_ims_send.state) return;  

  val _tg = getActions("telegram", tg_thing);  
  val _tg_status = getThingStatusInfo(tg_thing);
  if((null !== _tg) && (null !== _tg_status) && ("ONLINE" == _tg_status.getStatus().toString()))
    _tg.sendTelegram(bot_channel_private, _message);
end

Did I understand correctly (I checked) that the statuses of things are not enumerations like ON, OFF, CLOSED, etc. Do I need to convert them to a string for checking?

// correct
if( "ONLINE" == _tg_status.getStatus().toString())) ....

// incorrect
if( ONLINE === _tg_status.getStatus()) ....

It’s documented here: Actions | openHAB

All the built in actions are documented on that page.

Yes they are but they come from a different class. You need to call toString() or use the ThingStatusInfo Class in comparisons. The docs show using toString().

The result is of type ThingStatusInfo. It contains Thing Status, Status Details and Status Description. Refer to Thing Status API for how to get those information. If you just want to know the status, you can use thingStatusInfo.getStatus().toString() and the result will be one of the values in Thing Status.

If the thing is removed or it’s not added yet, it’ll return null.

Yes.

I wrote it incorrectly.
I meant that you need to specify everywhere that it may not be available at a certain moment (Startup, Shutdown, Init) and you must check the object for null, and for the state. In principle, when you refer to something (items, things, transform, etc.) in the rules, you need to somehow describe how to handle null, NULL, UNDEF, ONLINE, etc. Of course, when you gain experience in DSL, you start writing more carefully, but this is time-consuming.

ThingStatus.ONLINE - it doesn’t work. How to use such enum? I don’t understand how to access static members of a class ThingStatus.

If it is a Thing action, that needs to be documented by that binding because all of those are details about how that specific binding was written.

You must always check for null for Thing actions. The Thing can go offline at any time.

You’d have to import that class. I’m pretty sure it is not among the defaults imports. If it is imported though you’d just use ONLINE. You don’t use OnOffType.ON to get to the ON enum afterall. This would work the same if it is imported.

I’m not sure what using the enums buys you in this case if you have to import the class and recommend using toString.

Thank you, you explained everything clearly. I rewrote all the rules based on my new knowledge. It was very instructive, thanks again for your attention.