www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - FileException Inconsistency?

reply Alex <alexander.edwards gmail.com> writes:
Hi,

After too many years of thinking to try D, I'm creating a simple 
directory scanner.  However, I'm already getting some 
"inconistencies" in Exception handling I'm confused by and trying 
to debug.

I'm using Visual D on Windows. DMD 2.110

In the code below, if I leave the writeln(entry.name) or even 
just writeln(".") uncommented, then I get a FileException for 
permission-denied directories as expected.

If I comment these lines out, e.g. to add the entry.name to an 
array, instead I get a Throwable std.file.FileException which is 
not caught as a FileException or Exception.

Is this expected?  How to best handle this?

Thanks

```
module DirScan;

import std.stdio;
import std.file;
import std.algorithm;
import std.array;
import core.thread;

import std.compiler;

int main()
{
     writeln("DirScan\n");
     writefln("Compiler: %s v%d.%03d", name, version_major, 
version_minor);

     string path = "C:\\";

     scan(path);

     writeln("End. Press Enter.");
     readln();
     return 0;
}

void scan(string path)
{
     writeln("-----");
     writeln("PATH: ", path);

     foreach (DirEntry entry; dirEntries(path, SpanMode.shallow))
     {
         try
         {
             writeln(entry.name);
             writeln(".");

             if (entry.isDir)
                 scan(entry.name);
         }
         catch (FileException fe) {
             writeln("File Exception: ", fe);
             readln();
             continue;
         }
         catch (Exception e) {
             writeln("Exception: ", e);
             readln();
             continue;
         }
         catch (Throwable t) {
             writeln("Throwable: ", t);
             readln();
             return;
         }
     }
}
```
May 10
next sibling parent reply Nick Treleaven <nick geany.org> writes:
On Monday, 11 May 2026 at 04:18:33 UTC, Alex wrote:
 Hi,

 After too many years of thinking to try D, I'm creating a 
 simple directory scanner.  However, I'm already getting some 
 "inconistencies" in Exception handling I'm confused by and 
 trying to debug.

 I'm using Visual D on Windows. DMD 2.110

 In the code below, if I leave the writeln(entry.name) or even 
 just writeln(".") uncommented, then I get a FileException for 
 permission-denied directories as expected.

 If I comment these lines out, e.g. to add the entry.name to an 
 array, instead I get a Throwable std.file.FileException which 
 is not caught as a FileException or Exception.

 Is this expected?  How to best handle this?
I don't think commenting those lines should affect anything, but I'm testing on Linux which may be different.
     foreach (DirEntry entry; dirEntries(path, SpanMode.shallow))
`dirEntries` itself can throw:
 Throws: FileException if the path directory does not exist or 
 read permission is denied.
So you need to wrap the `dirEntries` call in try/catch too for when a subdirectory can't be read.
May 11
parent Nick Treleaven <nick geany.org> writes:
On Monday, 11 May 2026 at 10:25:24 UTC, Nick Treleaven wrote:
 `dirEntries` itself can throw:

 Throws: FileException if the path directory does not exist or 
 read permission is denied.
So you need to wrap the `dirEntries` call in try/catch too for when a subdirectory can't be read.
Sorry, actually that isn't needed so long as the initial path can be read. The original call of `scan` should catch that exception.
May 11
prev sibling next sibling parent reply Nick Treleaven <nick geany.org> writes:
On Monday, 11 May 2026 at 04:18:33 UTC, Alex wrote:
         catch (FileException fe) {
             writeln("File Exception: ", fe);
You might want to change `fe` to `fe.msg`, otherwise it will print the stack trace too, which is confusing.
May 11
parent reply Alex <alexander.edwards gmail.com> writes:
On Monday, 11 May 2026 at 10:40:27 UTC, Nick Treleaven wrote:
 On Monday, 11 May 2026 at 04:18:33 UTC, Alex wrote:
         catch (FileException fe) {
             writeln("File Exception: ", fe);
You might want to change `fe` to `fe.msg`, otherwise it will print the stack trace too, which is confusing.
Thanks for your replies and tip on '.msg'. The code "works" but my concern is simply that the file permission access error resuts in either a desirable FileException if there's a writeln() around, or a brutish Throwable std.file.FileException if there isn't a writeln() for some reason. Just seems very strange that the FileException isn't created and handled the same both ways as I would expect. I don't think it should have to catch a Throwable and decode a std.file.FileException into what looks like should be caught by the catch (FileException fe) {} for the same error? Cheers
May 11
parent reply Nick Treleaven <nick geany.org> writes:
On Monday, 11 May 2026 at 11:11:07 UTC, Alex wrote:
 On Monday, 11 May 2026 at 10:40:27 UTC, Nick Treleaven wrote:
 On Monday, 11 May 2026 at 04:18:33 UTC, Alex wrote:
         catch (FileException fe) {
             writeln("File Exception: ", fe);
You might want to change `fe` to `fe.msg`, otherwise it will print the stack trace too, which is confusing.
Thanks for your replies and tip on '.msg'. The code "works" but my concern is simply that the file permission access error resuts in either a desirable FileException if there's a writeln() around, or a brutish Throwable std.file.FileException if there isn't a writeln() for some reason. Just seems very strange that the FileException isn't created and handled the same both ways as I would expect. I don't think it should have to catch a Throwable and decode a std.file.FileException into what looks like should be caught by the catch (FileException fe) {} for the same error?
It's very strange if a `Throwable` prints `FileException` but it isn't a `FileException`. The Windows implementation of DirEntry is: ```d property string name() const pure nothrow return scope { return _name; } property bool isDir() const pure nothrow scope { return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; } ... property uint attributes() const pure nothrow scope { return _attributes; } ... string _name; /// The file or directory represented by this DirEntry. ... uint _attributes; /// The file attributes from WIN32_FIND_DATAW. ``` So calling any of those does not throw a Throwable, and calling `writeln(string)` should not either. So if you are correct (sorry I don't have Windows to hand to test), possibly there is a compiler bug. Can you try to come up with a minimal example that triggers the bug?
May 11
parent reply Nick Treleaven <nick geany.org> writes:
On Monday, 11 May 2026 at 15:52:11 UTC, Nick Treleaven wrote:
 So if you are correct (sorry I don't have Windows to hand to 
 test), possibly there is a compiler bug. Can you try to come up 
 with a minimal example that triggers the bug?
Another thing to try is removing the `catch(Throwable)` part. That is for advanced use only AIUI. What happens then?
May 11
parent Alex <alexander.edwards gmail.com> writes:
On Monday, 11 May 2026 at 16:00:23 UTC, Nick Treleaven wrote:
 On Monday, 11 May 2026 at 15:52:11 UTC, Nick Treleaven wrote:
 So if you are correct (sorry I don't have Windows to hand to 
 test), possibly there is a compiler bug. Can you try to come 
 up with a minimal example that triggers the bug?
Another thing to try is removing the `catch(Throwable)` part. That is for advanced use only AIUI. What happens then?
Hi, If I remove the catch(Throwable) the program crashes, which is why I added it in the first place :) If I put a try {} around the dirEntries() aswell that does seem to catch FileException reliably. I wonder if the Exception handling is being affected by the laziness of the dirEntries to some extent. I'm still surprised this FileException is only being caught as a Throwable in this case. I'm not sure I can create a much more simple example to test. Though "scanning" the C:\ProgramData\ directory and looking out for the Packages subdirectory should trigger the problem. I'll have a bit more of a go. Thanks
May 11
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Monday, 11 May 2026 at 04:18:33 UTC, Alex wrote:
 Hi,

 After too many years of thinking to try D, I'm creating a 
 simple directory scanner.  However, I'm already getting some 
 "inconistencies" in Exception handling I'm confused by and 
 trying to debug.

 I'm using Visual D on Windows. DMD 2.110

 In the code below, if I leave the writeln(entry.name) or even 
 just writeln(".") uncommented, then I get a FileException for 
 permission-denied directories as expected.

 If I comment these lines out, e.g. to add the entry.name to an 
 array, instead I get a Throwable std.file.FileException which 
 is not caught as a FileException or Exception.

 Is this expected?  How to best handle this?

 Thanks

 ```
 module DirScan;

 import std.stdio;
 import std.file;
 import std.algorithm;
 import std.array;
 import core.thread;

 import std.compiler;

 int main()
 {
     writeln("DirScan\n");
     writefln("Compiler: %s v%d.%03d", name, version_major, 
 version_minor);

     string path = "C:\\";

     scan(path);

     writeln("End. Press Enter.");
     readln();
     return 0;
 }

 void scan(string path)
 {
     writeln("-----");
     writeln("PATH: ", path);

     foreach (DirEntry entry; dirEntries(path, SpanMode.shallow))
     {
         try
         {
             writeln(entry.name);
             writeln(".");

             if (entry.isDir)
                 scan(entry.name);
         }
         catch (FileException fe) {
             writeln("File Exception: ", fe);
             readln();
             continue;
         }
         catch (Exception e) {
             writeln("Exception: ", e);
             readln();
             continue;
         }
         catch (Throwable t) {
             writeln("Throwable: ", t);
             readln();
             return;
         }
     }
 }
 ```
If you are seeing "Throwable: std.file.FileException", then I suggest one of two things are true: 1. You are misreading the error. 2. The code above + edits you have outlined is not what you are actually executing. Sorry, but these are the only two options I see here. The above doesn't seem like it should ever fail differently if you change `writeln` calls (I tested locally with the exact steps, and I don't see the issue you identify). I am often also guilty of trying to "simplify" things for posting on the forums. Almost inevitably, I have missed some typo or some interpretation. The generated code that handles exception catching is rock-solid, and decades old. I don't believe there would be an issue with it. I suggest posting the code that *doesn't* work, and also the output when that command is given which is not as you expect. It's OK to truncate the output to the problem area (I imagine there's quite a bit of your hard drive structure in there), but at least show what is printed surrounding the error. At the very least, this should allow people to reproduce the issue. e.g. on my mac system, I modified your code a bit: ```d module DirScan; import std.stdio; import std.file; import std.algorithm; import std.array; import core.thread; import std.compiler; int main() { writeln("DirScan\n"); writefln("Compiler: %s v%d.%03d", name, version_major, version_minor); string path = "./"; scan(path); writeln("End. Press Enter."); readln(); return 0; } void scan(string path) { writeln("-----"); writeln("PATH: ", path); foreach (DirEntry entry; dirEntries(path, SpanMode.shallow)) { try { //writeln(entry.name); //writeln("."); if (entry.isDir) scan(entry.name); } catch (FileException fe) { writeln("File Exception: ", fe); readln(); continue; } catch (Exception e) { writeln("Exception: ", e); readln(); continue; } catch (Throwable t) { writeln("Throwable: ", t); readln(); return; } } } ``` And then created a directory structure that is not readable: ```console % ls -R . foo testme testme.d testme.o ./foo: blah blah.txt ./foo/blah: ls: ./foo/blah: Permission denied ``` And this is the result I get: ```console % ./testme DirScan Compiler: LDC v2.111 ----- PATH: ./ ----- PATH: ./foo ----- PATH: ./foo/blah File Exception: std.file.FileException std/file.d(4752): ./foo/blah: Permission denied ---------------- ??:? safe bool std.file.DirIteratorImpl.stepIn(immutable(char)[]) [0x104888a47] ??:? ref safe std.file.DirIteratorImpl std.file.DirIteratorImpl.__ctor!(immutable(char)[]).__ctor(immutable(char)[], std.file.SpanMode, bool) [0x10487cb53] ??:? ref safe core.internal.lifetime.emplaceRef!(std.file.DirIteratorImpl, std.file.DirIteratorImpl, immutable(char)[], std.file.SpanMode, bool).emplaceRef(ref std.file.DirIteratorImpl, ref immutable(char)[], ref std.file.SpanMode, ref bool).S core.internal.lifetime.emplaceRef!(std.file.DirIteratorImpl, std.file.DirIteratorImpl, immutable(char)[], std.file.SpanMode, bool).emplaceRef(ref std.file.DirIteratorImpl, ref immutable(char)[], ref std.file.SpanMode, ref bool).S.__ctor!().__ctor(ref immutable(char)[], ref std.file.SpanMode, ref bool) [0x10487caab] ??:? safe void core.internal.lifetime.emplaceRef!(std.file.DirIteratorImpl, std.file.DirIteratorImpl, immutable(char)[], std.file.SpanMode, bool).emplaceRef(ref std.file.DirIteratorImpl, ref immutable(char)[], ref std.file.SpanMode, ref bool) [0x10487ca03] ??:? safe std.file.DirIteratorImpl* core.lifetime.emplace!(std.file.DirIteratorImpl, immutable(char)[], std.file.SpanMode, bool).emplace(std.file.DirIteratorImpl*, ref immutable(char)[], ref std.file.SpanMode, ref bool) [0x10487c9a3] ??:? safe void std.typecons.SafeRefCounted!(std.file.DirIteratorImpl, 0).SafeRefCounted.RefCountedStore.initialize!(immutable(char)[], std.file.SpanMode, bool).initialize(ref immutable(char)[], ref std.file.SpanMode, ref bool) [0x10487c903] ??:? ref safe std.typecons.SafeRefCounted!(std.file.DirIteratorImpl, 0).SafeRefCounted std.typecons.SafeRefCounted!(std.file.DirIteratorImpl, 0).SafeRefCounted.__ctor!(immutable(char)[], std.file.SpanMode, bool).__ctor(ref immutable(char)[], ref std.file.SpanMode, ref bool) [0x10487c53f] ??:? ref trusted std.file._DirIterator!(false)._DirIterator std.file._DirIterator!(false)._DirIterator.__ctor(immutable(char)[], std.file.SpanMode, bool) [0x10487c4cb] ??:? std.file._DirIterator!(false)._DirIterator std.file.dirEntries!(false).dirEntries(immutable(char)[], std.file.SpanMode, bool) [0x104860eaf] ??:? void DirScan.scan(immutable(char)[]) [0x104860b73] ??:? void DirScan.scan(immutable(char)[]) [0x104860bdf] ??:? void DirScan.scan(immutable(char)[]) [0x104860bdf] ??:? _Dmain [0x10486095b] End. Press Enter. ``` -Steve
May 11
parent reply Alex <alexander.edwards gmail.com> writes:
Thanks for trying to reproduce the error, though it is genuine.

I just literally created a fresh Visual Studio Project on Windows 
with the copied text. Here is the unmodified code running, with 
writelns.


```
DirScan

Compiler: Digital Mars D v2.110
-----
PATH: C:\
C:\$Recycle.Bin
.
-----
PATH: C:\$Recycle.Bin
C:\$Recycle.Bin\S-1-5-18
.
-----
PATH: C:\$Recycle.Bin\S-1-5-18
File Exception: std.file.FileException std\file.d(4683): 
C:\$Recycle.Bin\S-1-5-18: Access is denied.
----------------
0x00007FF77A617997 in d_throwc
0x00007FF77A630B75 in  safe bool 
std.file.cenforce!(bool).cenforce(bool, scope lazy const(char)[], 
immutable(char)[], ulong)
0x00007FF77A61E506 in  safe bool 
std.file.DirIteratorImpl.stepIn(immutable(char)[])
0x00007FF77A6105EC in std.file.DirIteratorImpl.__ctor!string.this 
at C:\D\dmd-2.110.0\windows\bin\..\..\src\phobos\std\file.d(4809)
0x00007FF77A610ACB in 
core.internal.lifetime.emplaceRef!(std.file.DirIteratorImpl, 
std.file.DirIteratorImpl, string, std.file.SpanMode, 
bool).emplaceRef.S.__ctor!().this at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\druntime\import\core\internal\lifetime.d(38)
0x00007FF77A6102BC in 
core.internal.lifetime.emplaceRef!(std.file.DirIteratorImpl, 
std.file.DirIteratorImpl, string, std.file.SpanMode, 
bool).emplaceRef at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\druntime\import\core\internal\lifetime.d(57)
0x00007FF77A61024D in 
core.lifetime.emplace!(std.file.DirIteratorImpl, string, 
std.file.SpanMode, bool).emplace at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\druntime\import\core\lifetime.d(64)
0x00007FF77A610137 in 
std.typecons.SafeRefCounted!(std.file.DirIteratorImpl, 
RefCountedAutoInitialize.no).SafeRefCounted.RefCountedStore.initialize!(string,
std.file.SpanMode, bool).initialize at
C:\D\dmd-2.110.0\windows\bin\..\..\src\phobos\std\typecons.d(7686)
0x00007FF77A610088 in 
std.typecons.SafeRefCounted!(std.file.DirIteratorImpl, 
RefCountedAutoInitialize.no).SafeRefCounted.__ctor!(string, 
std.file.SpanMode, bool).this at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\phobos\std\typecons.d(7789)
0x00007FF77A60F679 in 
std.file._DirIterator!false._DirIterator.this at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\phobos\std\file.d(4889)
0x00007FF77A60F613 in std.file.dirEntries!false.dirEntries at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\phobos\std\file.d(5011)
0x00007FF77A5F11AE in DirScan.scan at 
C:\Users\alexf\source\repos\DirScan2\DirScan2.d(30)
0x00007FF77A5F127C in DirScan.scan at 
C:\Users\alexf\source\repos\DirScan2\DirScan2.d(38)
0x00007FF77A5F127C in DirScan.scan at 
C:\Users\alexf\source\repos\DirScan2\DirScan2.d(38)
0x00007FF77A5F10A0 in D main at 
C:\Users\alexf\source\repos\DirScan2\DirScan2.d(18)
0x00007FF77A624223 in void rt.dmain2._d_run_main2(char[][], 
ulong, extern (C) int 
function(char[][])*).runAll().__lambda_L515_C29()
0x00007FF77A6240B4 in void rt.dmain2._d_run_main2(char[][], 
ulong, extern (C) int function(char[][])*).tryExec(scope void 
delegate())
0x00007FF77A62417F in void rt.dmain2._d_run_main2(char[][], 
ulong, extern (C) int function(char[][])*).runAll()
0x00007FF77A6240B4 in void rt.dmain2._d_run_main2(char[][], 
ulong, extern (C) int function(char[][])*).tryExec(scope void 
delegate())
0x00007FF77A623F8A in d_run_main2
0x00007FF77A6181A9 in d_run_main
0x00007FF77A5F23B2 in DirScan._d_cmain!().main at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\druntime\import\core\internal\entrypoint.d(29)
0x00007FF77A6A95C9 in invoke_main at 
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(79)
0x00007FF77A6A94E2 in __scrt_common_main_seh at 
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(288)
0x00007FF77A6A939E in __scrt_common_main at 
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(331)
0x00007FF77A6A963E in mainCRTStartup at 
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp(17)
0x00007FF98393E8D7 in BaseThreadInitThunk
0x00007FF985C0C3FC in RtlUserThreadStart
```

Then I comment out the 2 writelns ...

```
DirScan

Compiler: Digital Mars D v2.110
-----
PATH: C:\
-----
PATH: C:\$Recycle.Bin
-----
PATH: C:\$Recycle.Bin\S-1-5-18
Throwable: std.file.FileException std\file.d(4683): 
C:\$Recycle.Bin\S-1-5-18: Access is denied.
----------------
0x00007FF60B7475F7 in d_throwc
0x00007FF60B7609E5 in  safe bool 
std.file.cenforce!(bool).cenforce(bool, scope lazy const(char)[], 
immutable(char)[], ulong)
0x00007FF60B74E166 in  safe bool 
std.file.DirIteratorImpl.stepIn(immutable(char)[])
0x00007FF60B74050C in std.file.DirIteratorImpl.__ctor!string.this 
at C:\D\dmd-2.110.0\windows\bin\..\..\src\phobos\std\file.d(4809)
0x00007FF60B7409EB in 
core.internal.lifetime.emplaceRef!(std.file.DirIteratorImpl, 
std.file.DirIteratorImpl, string, std.file.SpanMode, 
bool).emplaceRef.S.__ctor!().this at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\druntime\import\core\internal\lifetime.d(38)
0x00007FF60B7401DC in 
core.internal.lifetime.emplaceRef!(std.file.DirIteratorImpl, 
std.file.DirIteratorImpl, string, std.file.SpanMode, 
bool).emplaceRef at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\druntime\import\core\internal\lifetime.d(57)
0x00007FF60B74016D in 
core.lifetime.emplace!(std.file.DirIteratorImpl, string, 
std.file.SpanMode, bool).emplace at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\druntime\import\core\lifetime.d(64)
0x00007FF60B740057 in 
std.typecons.SafeRefCounted!(std.file.DirIteratorImpl, 
RefCountedAutoInitialize.no).SafeRefCounted.RefCountedStore.initialize!(string,
std.file.SpanMode, bool).initialize at
C:\D\dmd-2.110.0\windows\bin\..\..\src\phobos\std\typecons.d(7686)
0x00007FF60B73FFA8 in 
std.typecons.SafeRefCounted!(std.file.DirIteratorImpl, 
RefCountedAutoInitialize.no).SafeRefCounted.__ctor!(string, 
std.file.SpanMode, bool).this at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\phobos\std\typecons.d(7789)
0x00007FF60B73F599 in 
std.file._DirIterator!false._DirIterator.this at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\phobos\std\file.d(4889)
0x00007FF60B73F533 in std.file.dirEntries!false.dirEntries at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\phobos\std\file.d(5011)
0x00007FF60B7211AE in DirScan.scan at 
C:\Users\alexf\source\repos\DirScan2\DirScan2.d(30)
0x00007FF60B721221 in DirScan.scan at 
C:\Users\alexf\source\repos\DirScan2\DirScan2.d(38)
0x00007FF60B721221 in DirScan.scan at 
C:\Users\alexf\source\repos\DirScan2\DirScan2.d(38)
0x00007FF60B7210A0 in D main at 
C:\Users\alexf\source\repos\DirScan2\DirScan2.d(18)
0x00007FF60B754093 in void rt.dmain2._d_run_main2(char[][], 
ulong, extern (C) int 
function(char[][])*).runAll().__lambda_L515_C29()
0x00007FF60B753F24 in void rt.dmain2._d_run_main2(char[][], 
ulong, extern (C) int function(char[][])*).tryExec(scope void 
delegate())
0x00007FF60B753FEF in void rt.dmain2._d_run_main2(char[][], 
ulong, extern (C) int function(char[][])*).runAll()
0x00007FF60B753F24 in void rt.dmain2._d_run_main2(char[][], 
ulong, extern (C) int function(char[][])*).tryExec(scope void 
delegate())
0x00007FF60B753DFA in d_run_main2
0x00007FF60B747E09 in d_run_main
0x00007FF60B7222D2 in DirScan._d_cmain!().main at 
C:\D\dmd-2.110.0\windows\bin\..\..\src\druntime\import\core\internal\entrypoint.d(29)
0x00007FF60B7D94E9 in invoke_main at 
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(79)
0x00007FF60B7D9402 in __scrt_common_main_seh at 
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(288)
0x00007FF60B7D92BE in __scrt_common_main at 
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(331)
0x00007FF60B7D955E in mainCRTStartup at 
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp(17)
0x00007FF98393E8D7 in BaseThreadInitThunk
0x00007FF985C0C3FC in RtlUserThreadStart
```

I may be new to D, but not programming in general. This 
inconsisitency bemuses me too.

This may be an issue specific to Windows perhaps, rather than 
your Mac etc.  I tried the LDC Windows compiler with same 
results.  GDC Windows didn't want to compile.

Putting a try{} around the dirEntries() too seems to resolve this 
to a proper FileException rather than Throwable, but still a bit 
surprised at this.
May 11
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Tuesday, 12 May 2026 at 04:35:41 UTC, Alex wrote:
 Thanks for trying to reproduce the error, though it is genuine.

 I just literally created a fresh Visual Studio Project on 
 Windows with the copied text. Here is the unmodified code 
 running, with writelns.
...
 I may be new to D, but not programming in general. This 
 inconsisitency bemuses me too.

 This may be an issue specific to Windows perhaps, rather than 
 your Mac etc.  I tried the LDC Windows compiler with same 
 results.  GDC Windows didn't want to compile.

 Putting a try{} around the dirEntries() too seems to resolve 
 this to a proper FileException rather than Throwable, but still 
 a bit surprised at this.
OK, I'm on board. This seems like an issue with codegen. Thank you for pasting the full output, this will help to reproduce. In the meantime, can you try some things? 1. Try from the command line (not visual D). 2. Download the latest DMD (2.110 is 2 versions behind) and try again. I'll try on my windows system and see if I can reproduce it. -Steve
May 12
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Tuesday, 12 May 2026 at 16:11:23 UTC, Steven Schveighoffer 
wrote:
 I'll try on my windows system and see if I can reproduce it.
Got around to this, I can reproduce. ``` PATH: C:\$Recycle.Bin ----- PATH: C:\$Recycle.Bin\S-1-5-18 Throwable: std.file.FileException std\file.d(4696): C:\$Recycle.Bin\S-1-5-18: Access is denied. ``` Will do some more research here, this should not happen. -Steve
May 12
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Wednesday, 13 May 2026 at 01:09:01 UTC, Steven Schveighoffer 
wrote:

 Will do some more research here, this should not happen.
I can confirm this is a compiler bug, specifically the compiler is eliding the exception catches. Likely because it thinks something here is nothrow (which the compiler will expect never throws an exception, only Error or Throwable). Still trying to narrow it down to a minimal case. -Steve
May 12
prev sibling parent reply Alex <alexander.edwards gmail.com> writes:
Hi,

 From commandline I get similar behaviour Exception vs Throwable.

with
```
writeln(entry.name);
writeln(".");
```

```
DirScan

Compiler: Digital Mars D v2.110
-----
PATH: C:\
C:\$Recycle.Bin
.
-----
PATH: C:\$Recycle.Bin
C:\$Recycle.Bin\S-1-5-18
.
-----
PATH: C:\$Recycle.Bin\S-1-5-18
File Exception: std.file.FileException std\file.d(4683): 
C:\$Recycle.Bin\S-1-5-18: Access is denied.
----------------
0x00B84F9E
0x00B7261A
0x00B72515
0x00B724E5
0x00B723E1
0x00B71FFF
0x00B71FAC
0x00B610B0
0x00B61111
0x00B61111
0x00B61040
0x00B7CA1F
0x00B7C88E
0x00B761F4
0x00B61A47
0x753F5D49 in BaseThreadInitThunk
0x7795D83B in RtlInitializeExceptionChain
0x7795D7C1 in RtlGetAppContainerNamedObjectPath
```

vs lines commented out
```
// writeln(entry.name);
// writeln(".");
```

```
DirScan

Compiler: Digital Mars D v2.110
-----
PATH: C:\
-----
PATH: C:\$Recycle.Bin
-----
PATH: C:\$Recycle.Bin\S-1-5-18
Throwable: std.file.FileException std\file.d(4683): 
C:\$Recycle.Bin\S-1-5-18: Access is denied.
----------------
0x00AE4F26
0x00AD25C6
0x00AD24C1
0x00AD2491
0x00AD238D
0x00AD1FAB
0x00AD1F58
0x00AC10B0
0x00AC10F5
0x00AC10F5
0x00AC1040
0x00ADC987
0x00ADC7F6
0x00AD60C4
0x00AC19F3
0x753F5D49 in BaseThreadInitThunk
0x7795D83B in RtlInitializeExceptionChain
0x7795D7C1 in RtlGetAppContainerNamedObjectPath
```

I just installed 2.112 and thought for a second it was fixed, but 
same "issue" (writelns commented out).

```
// writeln(entry.name);
// writeln(".");
```

```
DirScan

Compiler: Digital Mars D v2.112
-----
PATH: C:\
-----
PATH: C:\$Recycle.Bin
-----
PATH: C:\$Recycle.Bin\S-1-5-18
Throwable: std.file.FileException std\file.d(4696): 
C:\$Recycle.Bin\S-1-5-18: Access is denied.
----------------
0x000A40EE
0x000922F9
0x000922C9
0x000921AD
0x00091D9F
0x00091D4C
0x000810B0
0x000810F5
0x000810F5
0x00081040
0x0009B24F
0x0009B0CF
0x00095964
0x00081587
0x753F5D49 in BaseThreadInitThunk
0x7795D83B in RtlInitializeExceptionChain
0x7795D7C1 in RtlGetAppContainerNamedObjectPath
0x00000000
```

If the writelns are uncommented out, get a FileException 
correctly as before.

Thanks
May 12
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Wednesday, 13 May 2026 at 01:23:38 UTC, Alex wrote:

 If the writelns are uncommented out, get a FileException 
 correctly as before.
So what I've been using is a feature of the compiler that prints out a string representation of the AST. This is the `-vcg-ast` switch. Looking at that, the catch clauses for `FileException` and `Exception` are missing when `writeln` is in the loop. I have also discovered that it's not specific to writeln, and that *any* throwing (or possibly throwing) function will do. For example: ```d void foo() {} // not nothrow void fooNothrow() nothrow {} // this will catch the FileException: try { foo(); if (entry.isDir) scan(entry.name); } // this will catch the Throwable: try { fooNothrow(); if (entry.isDir) scan(entry.name); } ``` Clearly, the compiler is misjudging this code as nothrow. I think I can get a small reproducible example that does not need all the extra complication. But need some time to narrow it down. -Steve
May 12
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Wednesday, 13 May 2026 at 01:44:22 UTC, Steven Schveighoffer 
wrote:
 On Wednesday, 13 May 2026 at 01:23:38 UTC, Alex wrote:

 If the writelns are uncommented out, get a FileException 
 correctly as before.
So what I've been using is a feature of the compiler that prints out a string representation of the AST. This is the `-vcg-ast` switch. Looking at that, the catch clauses for `FileException` and `Exception` are missing when `writeln` is in the loop.
Should have said when `writeln` is *NOT* in the loop. -Steve
May 12
prev sibling next sibling parent Alex <alexander.edwards gmail.com> writes:
Thanks for investigating. Glad I found something "interesting" 
and not just "me" :)
May 12
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Wednesday, 13 May 2026 at 01:44:22 UTC, Steven Schveighoffer 
wrote:
 I think I can get a small reproducible example that does not 
 need all the extra complication. But need some time to narrow 
 it down.
This is extremely surprising, that this has never been seen before. I have confirmed that this is a long standing issue, back at least as far as version 2.068 (as far back as run.dlang.io has). I can confirm this is a problem on all OSes and architectures (the minimal example shows the problem on macos ldc2 as well). The pattern is that if you recurse on a non-template function, where the recursion throws an exception, and you try to catch that exception in the function, it does not emit the catch clauses unless they are Throwable (and presumably Error). Reproduction case: ```d import std.stdio; void poison() {} void foo(bool shouldthrow) { if(shouldthrow) { throw new Exception("here"); } try { // poison(); foo(true); } catch(Exception e) { writeln("caught exception"); } catch(Throwable t) { writeln("caught throwable"); } } void main() { foo(false); } ``` This prints "caught throwable" in all versions of dmd back to 2.068 (and presumably earlier), and also on ldc. if the call to `poison` is uncommented, then it prints "caught exception". So it's a front-end semantic bug. My hypothesis is that the compiler has temporarily marked `foo` as `nothrow` before finishing semantic, and therefore when it recurses, it thinks this cannot possibly throw an `Exception`. Therefore, it elides the catch clause for `Exception`. Thank you for persisting on this, I would not have expected this outcome. I will file a bug report on it. -Steve
May 12
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Wednesday, 13 May 2026 at 02:30:29 UTC, Steven Schveighoffer 
wrote:

 I will file a bug report on it.
Apparently a long standing bug. https://github.com/dlang/dmd/issues/17906 Regression since 2.066. I'll add your case and mine to the bug report. -Steve
May 12