Returning a sum of success + error, as e.g. Rust's Serde[0] or Elm's Json.Decode[1] do.
The developer gets the feedback that the operation has failed at runtime, but they also get the feedback that the operation can fail and they must handle it somehow at compile-time, even ignoring the issue is an explicit decision.
[0] https://docs.serde.rs/serde_json/de/fn.from_str.html
[1] http://package.elm-lang.org/packages/elm-lang/core/latest/Js...
In Play this is actually something like JsSuccess/JsError, not an exception, although Play is pragmatic (aka dirty) at times, so it's possible that it has functions throwing exceptions.
In more detail:
It's purely functional, so it cannot handle I/O at all on its own. It needs some sort of harness into which it fits which handles that, and which then calls the pure functions in Elm / Grain. In Elm's case that harness is JavaScript, and it seems to be a similar case for Grain. (Maybe for Grain it's WebAssembly? I don't honestly know enough about Web Dev to say).
During I/O (including user input, and sending data over a network), runtime exceptions can still be thrown, but once it's been converted and passed into the functional part, it's in a sense correctly formatted by definition, and the purely functional Elm / Grain parts don't throw runtime exceptions at all.
I'm curious how they handle JSON parsing. For example in Scala/Play you define the class you want to parse to, and if the JSON passed in at runtime doesn't match the shape of that class then you get a runtime exception. Obviously it'd be nice if that didn't happen, but I can't think of any alternative that would make sense.