Using :return from :onerror in= command block

I was trying to rewrite some code using the new :onerror statement and stumbled into this:

[admin@labISP] > :global fx do={ :onerror e in={:return (10 / $1) } do={ :error ("error executing $0 $1: $e")}}                         
[admin@labISP] > :put [$fx 0]
error executing $fx 0: division by zero
[admin@labISP] > :put [$fx 5] 
;false
[admin@labISP] > :global fx do={ :local r; :onerror e in={:set r (10 / $1) } do={ :error ("error executing $0 $1: $e")}; return $r} 
[admin@labISP] > :put [$fx 5]
2

Is this a bug or the statement :return cannot be used within the in={} block?

One man’s bug is another man’s feature… so IDK.

My view is the operators are also functions, so they return values. In the case of [:onerror], it returns a boolean if there was an error (false = no error/do={} run / true = error inside in={}). Since :onerror returns bool if error… that means the return value from the in={} block has no place to go. But this does allow :onerror to be used in a :if statement to test for an “wrapped” error… so that the logic of it I think.

So your :local x; :onerror …; :return $x scheme is likely the solution. Now, avoiding errors by doing type checking/etc is still a better approach in a lot of cases. The main benefit of [:onerror] is you get the error text - which theoretically could be matched/handled further – rather than a script just failing and error in logs (or, blindly skipping all errors (e.g. on-error={}))

Actually :onerror returns value from do={} block if executed when error occurs or false if no error. This is not correctly stated for :onerror command in doc:


:onerror can return false (if there is no error) and true (if there is an error) values, so it can be used in :if condition statement scripts.

This quote implies that true value is always returned from :onerror if error occurs which is not true, it’s value from do={} block.

It easy to test with:

> :local fx do={ :local res [:onerror e in={:return (10 / $1) } do={ :put "error executing $0 $1: $e"; :return "test" }]; :return $res }; :put [$fx 0] 

error executing $fx 0: division by zero
test

For such reasons :return must be used with some value in do={} block if :error is not called in it (when script is not interrupted by error) because it can cause unpredicted behaviors later in script, see more at http://forum.mikrotik.com/t/conditional-not-boolean-bug-with-onerror-new-in-7-13/172543/19
And as stated by @Ammo above, in={} block has no return value, so variable needs to be declared before :onerror and set in={} block for later usage, even if is just return value of function, so :return with variable value needs to be called outside in={} block as is done in OP 2nd function code.

All true :wink:.

But I am more included to call it a bug that it :onerror does NOT return “true” when a do={} block is called. In one of the beta/rc (or maybe some stable), I think in={} did require some return value, but that’s not the case any more.

IMO it should just be consistent, either it always returns true/false OR it use the :return value from EITHER do={} or in={} — the half-way house @optio highlights is not helpful since that makes using :onerror with either :if or a function problematic…

I agree for consistent behavior for block returns. Also if block has no :return command, then :onerror is should return :nothing so that :return command is optional in blocks.

100%.

I just thought the do={} part was consistent, was my error here. Personally, :nothing be best from both do={} and in={} without an explicit return. Especially, since bool types have their own oddities.

Yes, that’s was my thought. No need to have mandatory return value when is not needed while handling error or when processing in={}.

I filed a ticket on this (SUP-174412), since something isn’t right here IMO. With a simplified example:

# corrent per docs, but still weird:
:put [:onerror e in={:return true} do={:return 1.1}]
# false

# wrong! the return value should be true, if docs are right…
:put [:onerror e in={:error "throw"} do={:return 1.1}]
# 1.0.0.1

# correct per docs, since do={} is run
:put [:onerror e in={:error "throw"} do={}]           
# true

# wrong, since there still be a return value from :onerror
:put [:onerror e in={:error "throw"} do={:error "here"}]
# here

# also wrong, since return type should be bool
:put [:typeof [:onerror e in={:error "throw"} do={:error "here"}]]
# here

I’d like [:nothing] :wink: - but even docs aren’t right.

Yes, it’s a mess with this returns in blocks…

Eather it should be always without :return command in blocks and :onerror should return true or false value respectively:

:local res [:onerror e in={<do someting without retrun>} do={<do someting without retrun>}]
:put $res

false or true

or (preferred) with optional :return
when has returns, like:

:local res [:onerror e in={<do someting>; :return "ok"} do={<do someting>; :return "error"}]
:put $res

ok or error

when not in any combination, like:

:local res [:onerror e in={<do someting>; return "ok"} do={<do someting without retrun>}]
:put $res

ok or <nothing>

P.S.
Propose them in ticket with optional :return for both blocks :slight_smile:

Seemingly the :onerror “eats” the :return statement and the function will work as one that has no :return statement.
In comparison within the :do {} on-error={} statement (and in others like :while, :if) the :return statement works as expected.
Is this on purpose? Kind of weird statement block.

[admin@labISP] > :global f do={:do { :return true } on-error={ :return 1.1}}
[admin@labISP] > :put [$f]
true
[admin@labISP] > :global f do={:onerror e in={ :return true } do={ :return 1.1}}
[admin@labISP] > :put [$f]
;false
[admin@labISP] > :global f do={ :local x "do something"; ("but");("nothing to return") } 
[admin@labISP] > :put [$f]
;nothing to return
[admin@labISP] >

This means that a :do {} on-error={} cannot be replaced with :onerror e in={} do={} and this makes updating old scripts much complicated.

But old method still works, so, why update uselessly?

Can't beat that reasoning.

Yeah I already put that in the ticket:

While I get idea of :onerror could be used in an :if statement (which docs highlight)…. But in reality it likely be better if :onerror always returned :nothing unless an explicit :return was used in EITHER the do={} or in={} block so :onerror be more convenient in a function (rather than :if) since you can use :onerror to guarantee a return value type from a function.

But at a min here, the docs are wrong.




True. The only downside of :do ... on-error={ ... } is the reason for error is lost, but a lot of case that may not matter and function params likely suggest what the error would be.

Now IMO it's better to protect against failure BEFORE getting to any on-error/[:onerror]. Not saying remove the error protection blocks... more if there are known cases where an error could happen, it be better to use an :if with :typeof or other checks on values to prevent it going to the on-error={} or [:onerror ... do={}] blocks in the first place.