Fetch often times out in script, never at command line, and not when I enable fetch logging

I recently upgraded to ROSv7 (long-term channel, v7.20.8). A script that had run forever under ROSv6 without errors now frequently throws this error:

2026-02-26 15:40:42 fetch,info Download from checkip.dyn.com FAILED: Idle timeout - waiting data
2026-02-26 15:40:43 script,error executing script DNSMadeEasy dynamic update2 from scheduler (DNSMadeEasy dynamic update2) failed, please check it manually
2026-02-26 15:40:43 script,error,debug failure: Idle timeout - waiting data (/tool/fetch; line 55)

Except that the error never happens when I type the fetch command interactively, and since the interactive command always completes in under a second, it doesn’t seem likely that it’s really being caused by a timeout. e.g.:

[admin@MikroTik4.felines.org] /tool/sniffer> /tool/fetch mode=http url="http://checkip.dyn.com/" output=user
status: finished
downloaded: 1KiB
total: 1KiB
duration: 1s
data: Current IP CheckCurrent IP Address: 79.152.67.82

And the error stopped when I added /system/logging/topics add fetch=memory.

And then came back when I removed the logging topic for fetch…

Any ideas?
many thanks,
-Jay

A guy calls the mechanic: "The car won't start. Tell me why without me taking it over to have it looked at."

1 Like

It’s clear that you’re being critical of me, but I don’t understand what the criticism is.

I’ve identified the exact RouterOS version, provided the command that is frequently (though, not always) failing when run within the script, but which I have never seen fail when executed manually. And I added the most curious detail of the apparent interaction with extra debug logging.

What is your criticism/ what further detail about my car would you like to see at your mechanic’s shop?

If it matters, here is the precise command in the script:

/tool fetch url="http://checkip.dyn.com/" output=file dst-path="/dyndns.checkip.html" idle-timeout=15

The script (per Netwatch with fetch stopped working after 7.13 - #21 by iv34531 ) has the “ftp” permission (previously, the fetch always failed).

If you want to adapt it to the current situation:

A guy calls the mechanic:
"I have a car of brand X with license plate ABCD bought on August 7, 2016, red in color and yellow mats.
The car won't start.
Tell me why without me taking it over to have it looked at the motor.
I already know the problem is the left headlight fuse,
so tell me the problem is the left headlight, and that's why it won't start.
Oh, but I'm only telling you this after another phone call, I've already checked that the battery is charged."

You complain that the script doesn't work, but you don't show it, then you come out AFTER that "maybe" netwatch is involved, but the first post doesn't mention anything about it.
Since you've already identified the problem, that instruction, then it must necessarily be that one,
without others checking the rest...

The answers are always appropriate for the type of question asked...

I still don’t understand you, and your answer is unhelpful. Maybe try to be a bit nicer.

I’m approaching 60 years old, was hacking this kind of stuff over 40 years ago, and am definitely not as smart or as correct now as I was then (health issues, getting older sucks, but it largely beats the alternative).

When I see people requesting help, if they’ve clearly not done any of their homework, and they’re just wanting someone to do the whole job for them, I just ignore them - I don’t bother with snide remarks. But If they’re just imperfect, but the question is legitimate, I may try to help. The world is a rough enough place without looking for places to criticize unnecessarily.

So, have I still failed to include something necessary, in which case, what, please? Or do you just get off on attacking people?

What @rextended meant was probably this:

Normally on the forum, in order for other members to be able to give assistance, and to avoid wasting time due to guessing, it's expected that some configuration export (with sensitive information redacted), as well network schema in some cases (this probably doesn't apply to your issue), are also provided. That would be the equivalence of "taking the car over to have it looked at by the mechanic".

Because you've not provided an export of your configuration, not even the full of your problematic script, you are effectively doing what @rextended posted in post #2:

No one is able to help you because people don't want to spend time to do guessing and psychic debugging.

I thought that in my original post I had implied that the syntax of the /tool fetch command in the script was effectively identical to that of the sample interactive command that I gave in that post.

Having received rextended’s criticism I did provide that line from the script.

Have I still failed to provide the needed information?

The configuration export is the output of /export hide-sensitive file=myconfig. See this thread:

Because many issues are NOT localized to a single point, but may occur due to the interaction of the different parts of the configuration. Also, as @rextended wrote above, you also did not provided information such as "the script is run by netwatch". Because script executed by netwatch can behave differently compared to when you run it from WinBox for example.

With the configuration export we could have seen that your script is invoked by netwatch, even if you failed to mention it.

Thank you @CGGXANNX .

The script isn’t run by netwatch; it’s run by the system scheduler. “netwatch” only came up due to referencing the need for the ftp permission for fetch in ROSv7 from the linked thread (which, when I pasted the link in, I had not expected the forum software to replace the link text with the name of the linked thread). I apologize for the confusion.

Here is the system scheduler configuration from /system/scheduler print detail which runs this script:

name="DNSMadeEasy dynamic update2" start-date=2026-02-18 start-time=14:17:26 interval=1m on-event=DNSMadeEasy dynamic update2 owner="admin"
policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon run-count=104 next-run=2026-02-27 10:35:26

Providing an entire configuration export would seem to add an awful lot of extraneous information, and someone (various someones :roll_eyes: ) decades ago early in my tech career told me to be more succint … :sweat_smile: (although that was a bit more in the tech-person-talking-to-business-person context).

Hm, also, even with hide-sensitive there is a lot of information in there that, ideally, wouldn’t be shared, so it’s a lot of work to properly sanitize the output.

Erm. I started manually further sanitizing the 800+ line configuration export, and .. no. I’ve been a security geek for decades, and that’s just Too Much Information.

I realize that RouterOS is monumentally powerful (that’s why we like it), and monumentally complicated (that’s why we love to hate it, while we still like it), and not super well documented (see above), and why a configuration output in certain very complex cases would be necessary, but I just can’t bring myself to post the whole even-hide-sensitive configuration export, nor to spend the huge effort to manually sanitize it.

In cases like this, I hope that the right balance is that, if there is some weird internal conflict possible in a MikroTik configuration that could cause an intermittent failure in a /tool fetch command ( Fetch - RouterOS - MikroTik Documentation it doesn’t seem that the overall configuration should much affect it), that instead of expecting everyone to open their coat wide from the start, more specific pieces of the config would be requested after.

If, as a result, people don’t want to help, so be it.

Again, thanks.

Or work the other way round:

  1. try the above in command line.
  2. then try putting that line (and that line only) inside a script and run the script, giving it the appropriate permissions

Is the result:
A) different?
B) the same (but the same line in your more complex script remains having a different behaviour)?

If #A there is an issue between running the same command interactively or through a script.
if #B there is an issue in your more complex script that makes it behave differently from the as simple as it can be script you tested.

I'm sorry, but you can't blame people for not wanting to help, it's your own fault for being completely uncooperative.

I already pointed out in my second post that if you don't show the script, no one can't understand the interaction of one line of code with the rest, and vice versa.

It's like, in the famous example, taking only the working car battery to the mechanic and saying, "I can't bring you the rest because I have confidential documents in the trunk of the car", but the mechanic just needs to check the engine,
he's not interested in rummaging through the trunk of the car...

It isn't at all implausible (at least in my mind) that an idle timeout actually occurs. Maybe something isn't ready. This has happened to me, and I had to rewrite the script to tolerate it.

Officially the scheduler waits for 5s at system startup, but I seem to recall that it's only documented for on-startup scheduling.

If this doesn't happen on startup, it's possible that you're changing something in your script. In Mikrotik-land a lot of things happen asynchronously, so the effects of a change may not be properly propagated or side effects not become available by the time the next things in your script are executed. Checking an IP is suspiciously something you would do when you expect to acquire a new address... A sort of improper but quick test for this can be done by putting a delay of a few seconds before the fetch.

Thank you @jaclaz , yes, I’ve done that.

…

Of, FFS. I’d done that - repeatedly, over a few days. And, until this morning, I’d never gotten anything but an instant success.

Today, I do occasionally get timeouts doing it manually.

Apologies to all for the wasted time - it appears to be just the dyn checkip service occasionally being overwhelmed, and likely there’s nothing at all wrong with my configuration, script, etc.

Out of sheer curiosity, I did try (on a normal browser) that site, and also here it seems laggy/slow intermittently, still I wouldn't say that it is 15 seconds laggy (the default should be 10 seconds, which is already plenty to get a few bytes).

Are you sure-sure that idle-timeout=15 means actually idle-timeout=15s? :woozy_face:

Yes, the idle-timeout is in seconds.

In my testing, the fetch either works within 1-2 seconds, or it doesn’t work at all. I’ve removed the idle-timeout now, since the default of 10s is more than sufficient.

I’ve also enhanced the script to loop over the fetch command up to 5 times before giving up; here is the relevant part of the script. (n.b. while I think I’ve got this right, I’m still waiting for the error to recur, to make sure that the error is caught and reported; if I find that I have to update the script further, I’ll post back again alter). Nope, the fetch, when it timed out, would still break the script. I’ve further updated the script to put the fetch in its own :do { … fetch … } sub-function, as I saw described at RouterOS Bug - Why Do These Simple Scripts Break? Cause of Mysterious "value:" Prompt?

:do {
:local unused [:do {
:set result [/tool fetch url="http://checkip.dyn.com" output=file dst-path="/dyndns.checkip.html" as-value]
} on-error={ :log warning "Update Dynamic DNS v3 fetch failed, attempt# $loopcount"; } ];
:if ($result->"status" = "finished") do={ :set loopsucceeded 1;} else={:set loopcount ($loopcount + 1)};
} while=($loopsucceeded=0 && $loopcount < 5);

#check that we didn't just repeatedly timeout
:if ($loopsucceeded=0) do={ :error "Update Dynamic DNS v3 failed, fetch timed out five times in a row"}
:if ($loopcount > 1) do={ :put "Update Dynamic DNS v3 fetch retried $loopcount times"}

#Okay, we got here, so the fetch (eventually) worked, get the result
:set result [/file get dyndns.checkip.html contents]

#parse the current IP result

I would insert a (meaningful) delay (let's say 5 seconds) between two successive attempts, probably also 2 seconds would be enough, but right now it seems to me like you are shooting all your five shots one after the other, in practice flooding the site.

Normally I’d agree with that philosophy. In the case of Dyn’s checkip service, which I imagine gets hit tens of thousands of times per second, and that trying to provoke a failure often doesn’t succeed (the following were all entered manually, using the up arrow key to instantly re-enter the previous command line, one right after another [obviously, with me figuring out /system clock get whatever after the first couple :roll_eyes: ] ):

[admin@MikroTik4.felines.org] /tool/sniffer> /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html" status: finished
downloaded: 1KiB
total: 1KiB
duration: 1s
[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get date]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
2026-02-27
status: finished
downloaded: 1KiB
total: 1KiB
duration: 1s
[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get date]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
2026-02-27
status: finished
downloaded: 1KiB
total: 1KiB
duration: 1s
[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get time]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
16:06:20
status: failed
failure: Idle timeout - waiting data
[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get time]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
16:06:31
status: finished
downloaded: 1KiB
total: 1KiB
duration: 1s
[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get time]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
16:06:33
status: finished
downloaded: 1KiB
total: 1KiB
duration: 1s
[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get time]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
16:06:35
status: finished
downloaded: 1KiB
total: 1KiB
duration: 1s
[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get time]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
16:06:36
status: finished
downloaded: 1KiB
total: 1KiB
duration: 1s
[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get time]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
16:06:41
status: finished
downloaded: 1KiB
total: 1KiB
duration: 1s
[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get time]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
16:06:43
status: finished
downloaded: 1KiB
total: 1KiB
duration: 0s
[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get time]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
16:06:44
status: finished
downloaded: 1KiB
total: 1KiB
duration: 0s
[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get time]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
16:06:46
status: finished
downloaded: 1KiB
total: 1KiB
duration: 1s
[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get time]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
16:06:48
status: finished
downloaded: 1KiB
total: 1KiB
duration: 0s

.. in this case if it fails, trying again right away seems to be okay.

Ah, OK :slightly_smiling_face: , the timeout in case of failure automatically regulates the flow:

[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get time]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
16:06:20
status: failed
failure: Idle timeout - waiting data
[admin@MikroTik4.felines.org] /tool/sniffer> :put [/system clock get time]; /tool fetch url="http://checkip.dyndns.com/" output=file dst-path="/dyndns.checkip.html"
16:06:31
status: finished
downloaded: 1KiB
total: 1KiB
duration: 1s

your script gets just the first successful attempt, and then exits the loop so the site gets only one request if successful or at most 5 requests spaced by 10-11 seconds.

A topic worth taking as a reference, you can see how it ended in the end...

a working example, with the correct commands is better...

{
    :local chkIP ""
    :local retIP 0.0.0.0

    :retry max=5 delay=2s command={:set chkIP ([/tool fetch url="http://checkip.dyndns.com/ip.html" as-value output=user]->"data")} \
                          on-error={}

    :if ($chkIP ~ "((25[0-5]|(2[0-4]|[01]\?[0-9]\?)[0-9])\\.){3}(25[0-5]|(2[0-4]|[01]\?[0-9]\?)[0-9])") do={
# actual schema inside the reply: "<html><head><title>Current IP Check</title></head><body>Current IP Address: %IP%</body></html>\r\n"
        :set retIP [:toip [:pick $chkIP ([:find $chkIP ": " -1] + 2) [:find $chkIP "</b" -1]] ]
        :put "IP fetched >$retIP<"
    } else={
        :put "IP not fetched"
    }

}

https://help.dyn.com/checkip-tool.html

Policies
Checks must be spaced 10 minutes apart to help reduce server load

1 Like