Folderitem.childat outofbounds error

Should this code ever generate an outofbounds error?

for i = 0 to imgFldr.Count()-1
  theImg = imgFldr.Childat(i)

I have a case where this works fine in 2019 R3.2, but breaks in 2020 R2.1. I wrote a quick test to see if it was a general issue, and it doesn’t seem to be, so it’s probably something happening in the application or with the specific folder/files being iterated over, but not sure where to start. I’d think since Count() is re-evaluated every loop, there’d be no way you could get a bad index, even if things were being deleted in the folder.

In the case above, at the time of the exception, the debugger reports:
imgFldr.Count = 68
i = 67

This is plain wrong.

Why () ?

Never ask a Count there

Also: please capitalize code when due. At first this looked as ChilDat !

Read there:

At last:

Var Dir_Cnt As Integer = imgFldr.Count-1

For i = 0 To Dir_Cnt // 0 if this is correct, else use 1 and remove -1 above


Well, not sure what “plain wrong” means – it compiles. It works on 2019, not on 2020 for some edge case – repeatedly.

The parens are likely a holdover from something in the past, but they are irrelevant. Sorry for not camel-casing. Agree that putting the count outside the loop is more efficient… unless you delete or add things inside the loop. That was my point – this should not fail. The debugger values reported leave me wondering how it is that this is out of bounds: no matter if you are 0- or 1-based, 67 in a list of 68 is not out of bounds. Something is wrong and I thought it might be useful to provide feedback, but it’s clearly an edge case and I expect will need some further elaboration, so I was looking for any community knowledge.

This always strikes me as a dangerous operation. If you are adding or deleting things inside the loop then the array index may not properly iterate over all elements. IMHO, if you think you may have to change things while inside the loop, my preference is to:

  • Create empty array or dictionary or whatever to collect data to change later
  • Iterate over original array/dictionary, accumulating data on what to change
  • After the original loop is done, post-process the new array/dictionary of changes to be made

And within the loop, be sure to consider / trap cases where array elements or what they point at (like folderitem children or class instances or whatever) are no longer valid even if the array index itself is still valid.

Just my .02

This does seem wrong. Do you get the same error with

for i = imgFldr.Count()-1 DownTo 0
  theImg = imgFldr.Childat(i)

Agree, and I’m not deleting or adding, though there are some operations on the files inside the loop, which is probably where I would start.
The problem here is, the Count in the line before indicates that there are 68 elements in the directory (which, in fact, there are). The next line gets an outofbounds exception with the index for .ChildAt() of 67. Regardless of anything else going on, I cannot see how that can be right. Moreover, this is consistently different between the 2019 and 2020 versions for this case (which I’m not arguing isn’t some sort of edge case – it clearly is). Something is different there… might be a nice bug to catch since there’s a case that makes it happen. But I can’t get it to reproducible for a feedback report with what I have so far.

Your “holdover”, not Xojo.

You do not talk about the Alias status (read the doc / link I provided for more details).

It works on 2019, not on 2020
This may say Xojo squashed a bug in the mean time (or changed an API, etc.).

What about invisibles files ?

Did you try to follow in the debugger with a break in
if i = 66 then
// debug break or point here
End If

no matter if you are 0- or 1-based, 67 in a list of 68

No, if it crash at 67 and you started at 0, this means i = 68 then OOB !
If you said 60 in a list of 68, yes the OOB can be surprising.

Wayne, bingo. It does not fail with DownTo.

For the record:

  • No invisible files in the directory
  • No aliases

The debugger reports i = 67 at the time of the exception and imgFldr.Count=68. I did put an if i = 68 in just before this, as you suggest, and it didn’t break before the exception; i <> 68 within the loop.

have you tried for each?

Var path As FolderItem = SpecialFolder.Desktop
For Each f As FolderItem In path.Children
  System.DebugLog f.NativePath
1 Like

Markus, this solution also works properly. Thanks!

If you never open that directory, maybe.

But, in my experience, macOS always have at least a .DS_Store (an invisible file) in each folder.

Windows 10 may also have invisible files (Thumbs.db / Encrypted I forgot).

You found a solution: glad to read that.

I was curious (and had some minutes to check), then I wrote:

Var i As Integer
Var f As FolderItem
Var Itm_Cnt As Integer = SpecialFolder.Desktop.Count - 1

For i = 0 To Itm_Cnt
  f = SpecialFolder.Desktop.ChildAt(i)
  Listbox1.AddRow f.Name

For i = Itm_Cnt DownTo 0
  f = SpecialFolder.Desktop.ChildAt(i)
  Listbox2.AddRow f.Name

Add two ListBoxes, run and get no error, only the files appears in the reverse order (and I have two invisibles files: .DS_Store and .localized).

Yes, I did this test as well on the folder that was causing the issue in order to try to reproduce it for a feedback case before posting here, and I couldn’t, which is why I think it’s some edge case. There’s some difference between 2019 and 2020 that makes it happen with the same code. In any event, Wayne and Markus’ solutions both work in 2020, so if anyone else has this issue, this thread should help. I don’t think I have enough for a feedback submission.

BTW: if you are on MacOS, you have to know that the returned file order is actually alphabetized (asc or desc depending on the way to read the folder contents).

But, with Big Sur (M1 machines, not tested on Intel), the order is undefined (not alphabetized).

So, if you need to get the files in alphabetized order, you have to do it by yourself.

well same error here on xojo 2022r41 … seems a bug not fixed
with api 1, folderitem.item(i) or with api 2 folderitem.childat(i) sometimes returns a nil folderitem !
if you loop with for…each folderitem.children then you don’t get the nil error
@Matthew_Dinmore1 did you report a bug or not ?

If you’re going to manipulate the files, do so out of the loop.

Use a single loop to quickly go through and find what needs to be modified.
Use a second loop to modify these files.

This way you’re not pulling the rug out from under your own feet.

I do recall a similar issue reading the Windows Registry. I do recall working around it abandoning the count of items and looping until catching the error in a try/catch. The difference was like 2 items too.

Long time ago I saw this kind of bug, since I do a small loop to “wake up” the system before do my real loop.

NoDerElt = Min(MyFolder.Count, 3) - 1 ’ iNbre = 0
For iNbre = 0 to NoDerElt ’ For Each MyItem in MyFolder.Children(False)
MyItem = MyFolder.ChildAt(iNbre, False) ’ If not(MyItem = Nil) Then iNbre = iNbre + 1
’ If iNbre = 3 Then Exit
Next iNbre
NoDerElt = MyFolder.Count - 1
For iNbre = 0 to NoDerElt
’ Procced with MyItem = MyFolder.ChildAt(iNbre, False)
Next iNbre

It seems I have less Nil Child when I do this pre-loop.