In the following situation:
#-------------------------------------
module Internal
struct WrongInput <: Exception end
f(_) = throw(WrongInput())
f(x::Int) = x
g(x) = f(x)
h(x) = g(x)
export h
end
#-------------------------------------
module Exposed
using ..Internal
struct NiceReport <: Exception end
p(x) = h(x)
q(x) = p(x)
welcome(x) =
try
q(x)
catch e
e isa Internal.WrongInput && rethrow(NiceReport())
rethrow(e)
end
export welcome
Base.showerror(io::IO, ::NiceReport) =
print(io, "Don't worry it's not that bad just try again <3")
end
#-------------------------------------
# User.
using .Exposed
my_u(x) = welcome(x)
my_v(x) = my_u(x)
my_w(x) = my_v(x)
my_w("hi")
User obtains the following stacktrace:
ERROR: LoadError: Don't worry it's not that bad just try again <3
Stacktrace:
[1] f(::String) <<
@ Main.Internal ./test.jl.jl:4 <<
[2] g(x::String) <<
@ Main.Internal ./test.jl.jl:6 <<
[3] h(x::String) <<
@ Main.Internal ./test.jl.jl:7 << Garbage (from user POV).
[4] p(x::String) << Useless for debugging: not a bug (in the lib).
@ Main.Exposed ./test.jl.jl:17 <<
[5] q(x::String) <<
@ Main.Exposed ./test.jl.jl:18 <<
[6] welcome(x::String) <<
@ Main.Exposed ./test.jl.jl:21 <<
[7] my_u(x::String) <<
@ Main ./test.jl.jl:37 <<
[8] my_v(x::String) <<
@ Main ./test.jl.jl:38 << *USEFUL* (from user POV).
[9] my_w(x::String) <<
@ Main ./test.jl.jl:39 <<
[10] top-level scope <<
@ ./test.jl.jl:41 <<
[11] top-level scope <<
@ REPL[1]:1 <<
in expression starting at ./test.jl:41
Is there a way I could reliably trim frames [1] to [6] so as not to bury them into the lib implementation detail? The relevant info for them only starts on frame [7], and the deepest the lib the further up they need to scroll so as to just read the report.
Using throw(NiceReport()) instead of rethrow(NiceReport()) does cut it exactly where it should, but then the caused by section still flushes the report up.
(currently investigating Base.current_exceptions)
(nah, it doesn't seem we can mutate the returned ExceptionStack, can we?)
I don't think so, but also, I think it's a bad idea to even try. A stack trace should contain the entire call stack so it's maximally usable for debuggning. Once you try messing with it to improve user friendliness, you should not be throwing un-caught exceptions, but instead print an error to stderr or something like that
Just printint to stderr would drop frames [7] to [11] which are useful to user IMO.
Also the purpose of rethrow(e) in case of unexpected exception is to really obtain the full stacktrace in case the lib itself needs debugging, so I wouldn't worry about this?
It's going to be difficult to determine beforehand what's useful to a user. What's to say the error isn't truly caused by something internal in your library?
When I say difficult, I mean that it's not possible at all. You can't have that information
This pattern does (IMO):
try
f()
catch e
e isa ExpectedExceptionType || rethrow(e) # Obtain full stracktrace for debugging.
# or handle e gracefully, forgetting about internal detail.
end
Ahh so you'd want something like Python's raise X from None. Yeah that makes sense
Don't know that one, but yeah why not :)
I wish to trim whichever part of the stack collected before the point I can assert that detail doesn't matter.
(and I take responsibility for this as the lib author ofc)
Ah yeah I see. I don't think that exists. But it would be nice to have
Siigh, should I request it you think or is there already relevant momentum in that direction?
I think Julia's errors have gotten very little attention in general
Alright. I'll report as a feat request unless someone else teaches us something about this today :)
(re)throw(e; afresh=true) or sth would be nice.
I'd really like a way to trim out noise from stacktraces too. The context I have this from is writing code like this:
macro outline(args...)
fargs = esc.(args[1:end-1])
body = args[end]
@gensym f
quote
$f($(fargs...),) = $body
@noinline $f($(fargs...),)
end
end
function my_sqrt(x)
if x < 0
@outline x throw(ArgumentError("my_sqrt does not take negative arguments, got $x"))
end
[...]
end
Here @outline automates the process of making a function barrier for me so that the error-path doesn't clog up the generated code for my_sqrt, which helps optimization, and then the value can be used in the error message without worrying about all the involved io/string operations.
But the downside is that it clogs up stacktraces with irrelevant information.
https://github.com/JuliaLang/julia/issues/62594.. let's see ¯\_(ツ)_/¯
Relevant: https://github.com/JuliaLang/julia/issues/40138
Last updated: Aug 10 2026 at 05:52 UTC