Thanks — this is a thoughtful and technically grounded response, and I
appreciate the clarity around the trade-offs STS is managing. I think we
largely agree on the local reasoning behind each exception choice, and
your description of STS as a compromise object serving orthogonal
semantic needs resonates.
I want to clarify one aspect of my intent. I’m not so much offering
solutions here as asking questions — deliberately. In my experience,
strong scientific and engineering work starts by identifying where the
remaining uncertainty or friction actually is, before jumping to
remedies. Loom itself is a good example of that mindset.
One dimension that Loom brings into sharper focus is the role of the JVM
in shaping what becomes viable or idiomatic in Java. Making virtual
threads work required substantial runtime and tooling changes —
including around stack capture, continuations, debugging, and exception
mechanics — so that existing exception semantics continue to function
naturally under a very different execution model. That’s an impressive
achievement, but it also highlights an asymmetry.
Exceptions are a VM-native failure mechanism, so when the execution
model changes, the JVM absorbs the complexity needed to preserve their
ergonomics. Value-based failure modeling (e.g., Result-style returns) is
largely a library-level pattern: it doesn’t require JVM enhancements to
exist, but it also doesn’t receive first-class runtime or tooling
support in the same way. I’m not claiming Result<T,E> is free or
universally better — it has real costs in boilerplate and ergonomics,
especially in Java today — only that the ecosystem naturally gravitates
toward whatever the VM blesses as first-class.
This context is what motivates my broader question. Structured
concurrency makes lifetimes and scopes explicit, but failure and
cancellation semantics are still largely ambient and stack-oriented. STS
shows how much care is required to make that work well, yet it also
makes visible that there are still hard problems here, especially once
concurrency and parallelism are taken seriously:
• how cancellation should be modeled and propagated,
• how multiple concurrent failures should be aggregated or
prioritized,
• how to represent partial success and expected failure without
overloading “exceptional” paths,
• how failure semantics align with explicit lifetime scopes,
• and how observability and debugging scale across async and
parallel boundaries.
I’m not arguing that any particular exception in STS is wrong, nor
advocating a specific replacement model. I’m asking whether Loom’s
success suggests that error handling — like concurrency itself — may
still have unresolved design questions at the systems level, even if the
current answers are pragmatic and defensible.
If the conclusion is that exceptions remain sufficient even under these
constraints, that’s a reasonable position. My interest is in making that
reasoning explicit, in light of the new execution model Loom has introduced.
Thanks again for engaging seriously with the question.
Cheers,
Eric
On 2025-12-19 6:31 AM, David Alayachew wrote:
Hello @Eric Kolotyluk <mailto:[email protected]>,
Let me start off by giving context -- the way STS uses exceptions is a
little more complicated than just "throw, even on very much expected
errors".
One of the downsides of STS is that it is the hotelier to several
different guests with very different (almost orthogonal) semantic
needs -- thus forcing the final design to sardine them together in
some uncomfortable ways.
You mentioned one of these pain points in the previous thread -- about
the joiner returning null when successful, and exception otherwise.
Stuff like that is usually an indicator that an API is trying to do 2
or more things at once, and can't easily accomodate both in the
cleanest way. The literal reason java.lang.Void was created back when
was to bandaid this exact problem.
So, understanding that STS is trying to cover multiple different API
needs in one hood, hopefully that makes more sense why the answer is
null vs exception for that particular joiner. It's not clean, but it
serves the purpose well enough, imo.
With that context out of the way, let me respond to your points.
* How do unchecked exceptions interact with structured concurrency’s
goal of making lifetimes and failure scopes explicit?
I'm not sure I follow. Are you asking how unchecked exceptions thrown
by MY CLIENT CODE interact with STS? If so, I'd say, the same as
everywhere else.
My understanding is that Unchecked is for programming bugs, and
therefore, should not be dealt with. The only difference between other
contexts and STS is that, for some of the joiners (awaitAll), STS
gives you the choice to do that or not. It's not necessarily the
default to propagate, which some developers have raised disagreement
with in the past.
* Do exceptions remain the best abstraction for expected failure in
highly concurrent, compositional code?
Well, again, it depends what you mean here. This question and the one
before it are rather open-ended.
Currently, the join method throws several different exceptions.
WrongThreadException -- I think using an (unchecked) exception is the
right choice here because this situation can only occur via
programming error.
IllegalStateException -- Same logic as above.
FailedException -- Some feel this should be replaced by a return type
in the vein of Result<T> or something related. I don't necessarily
agree, as I still do want a stack trace with line numbers. And if that
Result<T> is actually Result<T,Ex> where Ex is an exception, well I
think Exceptions are the better vehicle for that type of response
instead of Result.
TimeoutException -- This is a great example of what I mean when I say
sardine. Normally, this would obviously be a checked exception (an
expected failure that no amount of prep time can realistically
prevent), but since I can turn off timeouts, forcing everyone to pay
for this doesn't make sense. Aka, sardines. But really, the original
sin is that code that doesn't do timeouts shouldn't be able to throw
this. Sadly, the only real way to do this in Java 25 is by
significantly bloating the Java api. You'd have to break apart and
duplicate the API in ways that increase the surface area while adding
very little semantic meaning. That's a double whammy in the worst way.
That'd be like Stream vs IntStream vs DoubleStream all over again. I
can definitely understand ehy they do not want that for STS. Maybe
some exploration is being done towards remedying this, idk.
InterruptedException -- Well, this one is fine. However you feel about
Interrupts and how Java implements them, STS is advertised to handle
and emit interrupts "properly", therefore the behaviour here is
unavoidable, according to the spec. You'd have to trandform STS into
something wildly different in order to change how or if we deal in
InterruptedExceptions.
So, from what I can see here, each of the exceptions seem reasonable.
Albeit, some are the result of conflicting concerns. But I don't see
how any other solution would address these better.
* Are there patterns (or emerging idioms) that Loom encourages which
mitigate long-standing concerns with exceptions — or does Loom
expose new ones?
* More broadly, should Java be thinking in terms of additional
failure-handling tools rather than a single dominant model?
I think Java already has, but even in light of that exploration, chose
to use exceptions here.
But frankly, both of these points are broad. I think you need to be
more specific here.
I will say, your original post in the previous thread was asking a
very different question than this thread. Did you mean to, or are you
building up to that?
On Fri, Dec 19, 2025, 8:25 AM David Alayachew
<[email protected]> wrote:
And just for context for all, here is the previous thread where
this discussion originated.
https://mail.openjdk.org/pipermail/loom-dev/2025-December/008117.html
You can start reading from there. A few more replies later, and
then this new thread was created, so as not to distract from the
other topic.
On Fri, Dec 19, 2025, 1:35 AM Eric Kolotyluk <[email protected]>
wrote:
Hi all,
I’m starting a new thread to continue a discussion that
emerged elsewhere, per mailing list etiquette, and to give the
topic a clean and traceable home.
My interest here isn’t reactive to any one exchange. I’ve been
experimenting with Loom since its early iterations, and over
time it has sharpened a concern I already had: whether Java’s
traditional exception model remains the right default
abstraction in a world of structured concurrency, virtual
threads, and large-scale composition.
To be clear, this is not a claim that “exceptions are broken”
or that Java should abandon them. Java’s exception system has
supported billions of lines of successful code, and I’ve used
it productively for decades. Rather, Loom makes certain
trade-offs more visible — particularly around control flow,
cancellation, failure propagation, and reasoning about
lifetimes — that were easier to ignore in a purely
thread-per-task world.
The core questions I’m interested in exploring are along these
lines:
* How do unchecked exceptions interact with structured
concurrency’s goal of making lifetimes and failure scopes
explicit?
* Do exceptions remain the best abstraction for expected
failure in highly concurrent, compositional code?
* Are there patterns (or emerging idioms) that Loom
encourages which mitigate long-standing concerns with
exceptions — or does Loom expose new ones?
* More broadly, should Java be thinking in terms of
additional failure-handling tools rather than a single
dominant model?
I’m not advocating a specific alternative here — just inviting
a technical discussion about whether Loom changes how we
should think about error handling, and if so, how.
That said, exposure to other ecosystems (e.g., Scala, Kotlin,
and more recently Rust) has broadened how I think about
failure modeling. One thing I’ve consistently appreciated
about Java is that it tends to integrate external ideas
deliberately, rather than reflexively rejecting them or
adopting them wholesale. Loom itself is a good example of that
approach.
I’m interested in whether error handling deserves a similar
re-examination in light of Loom’s goals.
Looking forward to the discussion.
Cheers,
Eric