Stream: helpdesk (published)

Topic: Trim exposed stracktraces.


view this post on Zulip iago-lito (Jul 31 2026 at 07:50):

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.

view this post on Zulip iago-lito (Jul 31 2026 at 07:52):

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.

view this post on Zulip iago-lito (Jul 31 2026 at 07:54):

(currently investigating Base.current_exceptions)

view this post on Zulip iago-lito (Jul 31 2026 at 07:59):

(nah, it doesn't seem we can mutate the returned ExceptionStack, can we?)

view this post on Zulip Jakob Nybo Andersen (Jul 31 2026 at 07:59):

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

view this post on Zulip iago-lito (Jul 31 2026 at 08:01):

Just printint to stderr would drop frames [7] to [11] which are useful to user IMO.

view this post on Zulip iago-lito (Jul 31 2026 at 08:02):

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?

view this post on Zulip Jakob Nybo Andersen (Jul 31 2026 at 08:02):

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?

view this post on Zulip Jakob Nybo Andersen (Jul 31 2026 at 08:02):

When I say difficult, I mean that it's not possible at all. You can't have that information

view this post on Zulip iago-lito (Jul 31 2026 at 08:03):

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

view this post on Zulip Jakob Nybo Andersen (Jul 31 2026 at 08:04):

Ahh so you'd want something like Python's raise X from None. Yeah that makes sense

view this post on Zulip iago-lito (Jul 31 2026 at 08:04):

Don't know that one, but yeah why not :)

view this post on Zulip iago-lito (Jul 31 2026 at 08:05):

I wish to trim whichever part of the stack collected before the point I can assert that detail doesn't matter.

view this post on Zulip iago-lito (Jul 31 2026 at 08:05):

(and I take responsibility for this as the lib author ofc)

view this post on Zulip Jakob Nybo Andersen (Jul 31 2026 at 08:09):

Ah yeah I see. I don't think that exists. But it would be nice to have

view this post on Zulip iago-lito (Jul 31 2026 at 08:10):

Siigh, should I request it you think or is there already relevant momentum in that direction?

view this post on Zulip Jakob Nybo Andersen (Jul 31 2026 at 08:20):

I think Julia's errors have gotten very little attention in general

view this post on Zulip iago-lito (Jul 31 2026 at 08:22):

Alright. I'll report as a feat request unless someone else teaches us something about this today :)

view this post on Zulip iago-lito (Jul 31 2026 at 08:25):

(re)throw(e; afresh=true) or sth would be nice.

view this post on Zulip Mason Protter (Jul 31 2026 at 15:09):

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.

view this post on Zulip iago-lito (Jul 31 2026 at 18:30):

https://github.com/JuliaLang/julia/issues/62594.. let's see ¯\_(ツ)_/¯

view this post on Zulip iago-lito (Jul 31 2026 at 20:53):

Relevant: https://github.com/JuliaLang/julia/issues/40138

view this post on Zulip iago-lito (Sep 14 2026 at 19:38):

Hi. I am puzzled that #62606 is obtaining zero feedback. Do you think I did anything wrong? Is it unclear that I even expect anything? Should I just blame it on the northern hemisphere summer period and probable community exhaustion around v1.13 release?
I was thinking maybe I should just very briefly unmark the PR as a "draft" so as to trigger formal review requests? But maybe that'd be considered rude?
Or maybe this is just usual latency within the project?

view this post on Zulip Andy Dienes (Sep 18 2026 at 02:33):

view this post on Zulip iago-lito (Sep 18 2026 at 04:39):

Thank you, that is informative, although yet.. disheartening ^ ^"
I'm not sure how to best move forward then. I'll maybe start by un-drafting it to emit a more formal review request. But I'm unsure whether to spent more effort to get further on my own without getting at least a positive vibe/hint about the approach as a whole.. ?

view this post on Zulip Jakob Nybo Andersen (Sep 18 2026 at 12:14):

Un-drafting is a good first step. To me, a draft PR means it's not ready for review yet.
I think it's a good idea to find some people who engaged in previous stacktrace discussions and ask them to review it. Probably people with merge rights are pressed for time in general and will be more likely to move forward with a PR if they can see that a few stakeholders agree this is the right direction

view this post on Zulip Jakob Nybo Andersen (Sep 18 2026 at 12:15):

As someone who hasn't gone deep into the stacktrace discussion, it's this long long issue discussed over years, with lots of conflicting opinions. It takes a herculean amount of effort even to understand all the arguments from all sides, and that's before you even have the background knowledge to have an opinion about any given PR.

view this post on Zulip iago-lito (Sep 18 2026 at 12:35):

Thank you for feedback. I am at least aware of this attempt having been discussed in the past. And I figured that most of the discussion was whether we should accept a heuristic approach to pruning stacktraces (I would not fwiw). I understand how we may not succeed in converging towards a consensus there. The approach I offer is exact instead, the trick being to put lib authors in charge. Are you aware of other past attempts / different strategies?

view this post on Zulip iago-lito (Sep 18 2026 at 12:38):

it's a good idea to find some people who engaged in previous stacktrace discussions

Well there is at least @BioTurboNick but he seemed satisfied with the heuristic approach and, err.. maybe sour about the idea of trying this again ^ ^"

But do you happen to know more people like this? Or else how to search?

view this post on Zulip cschen (Sep 18 2026 at 13:13):

I’ve found that bots are very good at exploring issues and PRs and give a summary of the “prior art”.
I usually ask to gather a list of relevant issues/PRs with a quick summary on the topic of the discussion that I can then read on my own.

view this post on Zulip Em (Sep 19 2026 at 06:09):

iago-lito said:

I am puzzled that #62606 is obtaining zero feedback

I agree with Jakob that the particular problem of stack frame hiding is a bit cursed, because the cost of getting it wrong (hiding a useful frame) is massive confusion, and the cost of leaving too many frames in is minor inconvenience. When you add the fact that the lower-complexity fewer-bugs option is just printing everything, you may understand the lack of motivation to hide frames. I can't express how much I appreciate your clear and well-written PR description, though.

whether we should accept a heuristic approach to pruning stacktraces (I would not fwiw).

We already have a few (kwcall, __repl_entry, possibly some in the backtrace machinery itself)


Last updated: Sep 19 2026 at 08:53 UTC