Rules DSL in 5.1 is now unloading rules if it has unreachable expressions?

Did something change in 5.1 for rules DSL parsing? Previously if you had a “return;” in the rule and it prevented further execution, it would print out a warning about unreachable expression and then it would print out a line that said “using it anyway”, which was fine – I meant to temporarily block out part of the rules.

Now I just noticed in one of my debug rules , that it is unloading the entire rule, for example:

2026-02-12 18:18:36.871 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading DSL model ‘debug.rules’
2026-02-12 18:18:38.659 [WARN ] [el.core.internal.ModelRepositoryImpl] - DSL model ‘debug.rules’ has errors, therefore ignoring it: Unreachable expression.
2026-02-12 18:18:38.661 [INFO ] [el.core.internal.ModelRepositoryImpl] - Unloading DSL model ‘debug.rules’

This was not listed as a breaking change AFAIK. Am I incorrect or did this work normally up until now?

There was a change. All validation diagnostics were previously considered as warnings.

Now validation diagnostics returned with an ERROR flag are now considered as errors .

Made sense to us when it was changed.

Should we made an exception for rules? To be discussed.

@jimtng for information

I like the behavior before.. I used this too.

For debugging the rule it’s nice with the return.

I vote for the old behavior :blush:

greets

1 Like

I just noticed that behavior too. Even worse it unloads all rules from that DSL rule file.

I vote for returnung to the old behavior.

Thanks

Thanks for confirming! You know I did actually look at that PR because it was listed in the notes, but I just didn’t understand the impact as related to putting a return; in the middle of the rules.

Maybe if it’s a handful of people leave it, but I only noticed it today and, as @ollo mentioned, it had taken out my tesla rules which I hadn’t noticed until now. An issue is that when you start openhab it may not be noticed with all the other logging - it looks like the messages are actually coming through as WARN not ERROR. Other ERROR type messages from rules do not invalidate the entire rule, e.g: an unresolved item in a trigger, so even if I noticed I might not pick it up right away. It was only today when I wanted to try out something that I went into my debug.rules and noticed they didn’t work. The way I use the DSL is that I keep a bunch of complicated DSL in a rule to refer to later on – I would tend to forget them. I just use comments and return; to block things out.

I changed all my return; ‘s to if (true) return; for now, but I do want mention it is still upset, but allows the rule to load like before. I think if there is a future change that is going to break if (true) return; it would merit an explicit mention under breaking changes.

2026-02-13 02:00:21.270 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading DSL model ‘debug.rules’
2026-02-13 02:00:24.359 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in DSL model ‘debug.rules’, using it anyway:
Constant condition is always true.


This can have severe impact on people with rules they never cared to get “warning-less”, possibly because they didn’t understand the validation warning or couldn’t find out how to fix it.
There’s some hard to catch ones, particularly those who don’t show a line number, and you have a large rule.

I’d guess there’s potentially a lot of affected users!
So yes, for now I’d request to undo that. cc @jimtng

Please keep in mind that any future update of underlying libraries such as Xtend/Xtext might generate new warnings for existing rules, causing existing configs to stop working.

Also I wonder why in this specific case a failing validation with clearly warning-only character (unreachable expression - so what?) returns an error? That’s a bug, too.

2 Likes

As of https://community.openhab.org/t/text-rules-are-randomly-not-loaded-in-openhab-5-1/ a rule with [ lamp | in OpenHAB 5.1 sometimes fails, and sometimes not, while [GenericItem lamp| always works. In 5.0 [ lamp | always worked. My understanding is that sometimes parsing a rule produces a warning/error:

There is no context to infer the closure's argument types from. Consider typing the arguments or put the closures into a typed context.

and sometimes not. In that case it is mentioned that during boot this warning/error appears, but creating the rule after booting does not produce the warning/error.

Why does the above error/warning happen only sometimes?

I figured this out. After boot, a rule containing

g.members.forEach[ u | logError(“A”, u.name + “|” + u.state + " | " + u.class.toString) ]

produces:

DSL model ‘starttest.rules’ has errors, therefore ignoring it: There is no context to infer the closure’s argument types from. Consider typing the arguments or put the closures into a typed context.

when itemg is not defined, and works fine, if GroupItem g is defined. So the cited warning/error happens (on boot) when the rules are parsed before the items are loaded.

If (Group) item g is not defined, and I rewrite the snippet to:

g.members.forEach[ logError(“A”, it.name) ]

on rule parsing there are no errors, at rule execution the error is

Script execution of rule with UID ‘starttest-1’ failed: The name ‘g’ cannot be resolved to an item or type; line 6, column 5, length 1 in starttest

and the rule is not unloaded. However

g.members.forEach[ u | logError(“A”, u.name) ]

produces during parsing

DSL model ‘starttest.rules’ has errors, therefore ignoring it: There is no context to infer the closure’s argument types from. Consider typing the arguments or put the closures into a typed context.
Unloading DSL model ‘starttest.rules’

So my explanation is that when DSL Textual Rules are parsed during boot, they can produce There is no context to infer the closure’s argument types from. error/warning, if the rule utilizes for not yet loaded GroupItem g: g.members.forEach[ x |.

Can someone please post two versions of a .rules file that:

a. Works / gets loaded successfully
b. Doesn’t work / not loaded but you wish would have loaded. Ideally this is the same rule as A with just one little change that you feel shouldn’t cause it not to load.

Make sure it’s the full file that I can use to reproduce the behaviour.

I believe this case stands for all DSL Rule warnings converted to errors:

If (Group) item g does not exist:

  • With 5.1 fails during parsing, but was parsed and loaded with 5.0. In 5.0 it caused run-time error, in case g was still absent during execution:
rule "A"
when Item r changed then
    g.members.forEach[ u | logError("A", u.name) ]
end
  • Works (when item g does not exist, parsing completes, but there is an error during execution about g being undefined):
rule "A"
when Item r changed then
    g.members.forEach[ logError("A", it.name) ]
end
  • Same as above, works (when item g does not exist, parsing completes, but there is an error during execution about g being undefined):
rule "A"
when Item r changed then
    g.members.forEach[ GerenicItem u | logError("A", u.name) ]
end

Doesn’t work / not loaded but you wish would have loaded.

This are the first snippet above, and this one:

rule "A"
when Item r changed then
    return;
    g.members.forEach[ logError("A", it.name) ]
end

I missed the semicolon after the return, which produced:

DSL model ‘a.rules’ has errors, therefore ignoring it: Void functions cannot return a value.

However the intended test case is with semi-colon return; g.members.forEach[ logError("A", it.name) ], emitting

DSL model ‘a.rules’ has errors, therefore ignoring it: Unreachable expression.

Here one more, slightly different example:

val a = [ i | i.toString + " T" ]

rule "A"
when Item b changed then
    logError("A", a.apply(Integer.valueOf(7)))
end

produces during parsing There is no context to infer the closure's argument types from. Consider typing the arguments or put the closures into a typed context. It is slightly different case, compared to the parsing-time error in the rule-body, which works fine at runtime.

This works in openHAB 5.1, at execution it prints “7 T”:

val a = [ Integer i | i.toString + " T" ]

rule "A"
when Item b changed then
    logError("A", a.apply(Integer.valueOf(7)))
end

This also works and prints “7 T”.

val (Integer)=>String a = [ i | i.toString + " T" ]

rule "A"
when Item b changed then
    logError("A", a.apply(Integer.valueOf(7)))
end
  • Ideally, if val a = [ Integer i | i.toString + " T" ] emits a warning There is no context to infer the closure's argument types from. and not an error, then this syntax should work. It should work also outside of rule bodies, to make it consistent. (Otherwise the same lambda in a rule body will be parsed, but outside of a rule body will not be parsed - makes no sense).

I’m testing the fix now. Will open a PR soon

1 Like

Thanks!
On a sidenote, some of these validation warnings are shown but no line number.
Happens (only?) with lambdas/functions like that example some posts up here.
This sometimes makes it hard to get to the bottom of the cause to fix it.
Can you add line numbers?

Open a new issue on github. I’m not very familiar with rulesdsl internals but I’d imagine it should be possible.

Dear Lolodomo,
my suggestion would be :slight_smile:
a) show the issues in log to fix them when load the file first time …
(and for me load all not broken rules/etc)
b) or a kind of “rule compiler” to check on synthax etc before loading them. -
that applies for all textural configuration.

that Home Assistant does have a “check Config button” to initiate a chech and see all issues.

It has been fixed, previous behaviour has been restored. There will be a log but rule will be accepted.

3 Likes

A lot of affected users - true!