digitalmars.D.learn - FileException Inconsistency?
- Alex (64/64) May 10 Hi,
- Nick Treleaven (6/22) May 11 I don't think commenting those lines should affect anything, but
- Nick Treleaven (3/8) May 11 Sorry, actually that isn't needed so long as the initial path can
- Nick Treleaven (3/5) May 11 You might want to change `fe` to `fe.msg`, otherwise it will
- Alex (12/17) May 11 Thanks for your replies and tip on '.msg'.
- Nick Treleaven (30/47) May 11 It's very strange if a `Throwable` prints `FileException` but it
- Nick Treleaven (3/6) May 11 Another thing to try is removing the `catch(Throwable)` part.
- Alex (15/21) May 11 Hi,
- Steven Schveighoffer (144/208) May 11 If you are seeing "Throwable: std.file.FileException", then I
- Alex (179/179) May 11 Thanks for trying to reproduce the error, though it is genuine.
- Steven Schveighoffer (10/22) May 12 OK, I'm on board. This seems like an issue with codegen. Thank
- Steven Schveighoffer (12/13) May 12 Got around to this, I can reproduce.
- Steven Schveighoffer (8/9) May 12 I can confirm this is a compiler bug, specifically the compiler
- Alex (118/118) May 12 Hi,
- Steven Schveighoffer (31/33) May 12 So what I've been using is a feature of the compiler that prints
- Steven Schveighoffer (4/12) May 12 Should have said when `writeln` is *NOT* in the loop.
- Alex (2/2) May 12 Thanks for investigating. Glad I found something "interesting"
- Steven Schveighoffer (49/52) May 12 This is extremely surprising, that this has never been seen
- Steven Schveighoffer (7/8) May 12 Apparently a long standing bug.
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
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
On Monday, 11 May 2026 at 10:25:24 UTC, Nick Treleaven wrote:`dirEntries` itself can throw:Sorry, actually that isn't needed so long as the initial path can be read. The original call of `scan` should catch that exception.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
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
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: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? Cheerscatch (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
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: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?On Monday, 11 May 2026 at 04:18:33 UTC, Alex wrote: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?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
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
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: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. ThanksSo 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
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
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
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
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
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
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
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
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:Should have said when `writeln` is *NOT* in the loop. -SteveIf 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.
May 12
Thanks for investigating. Glad I found something "interesting" and not just "me" :)
May 12
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
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









Nick Treleaven <nick geany.org> 