onUncaughtError property
Called when an error escapes an interpreted callback that the platform invoked outside the script's own future chain.
A Stream.listen callback, a handleError handler and a Timer body are
all invoked by the platform, not by the script. When one of them throws,
native Dart sends the error to the current Zone and lets the enclosing
main() return normally — and d4rt matches that. The problem it leaves
behind is that Zone, runZoned and runZonedGuarded are deliberately
unbridged (see unbridged_reasons.dart), so an interpreted script has no
way at all to observe its own callback failing. This hook is the
embedder's way in.
The error handed to the hook is the value the script actually threw. The
interpreter's internal InternalInterpreterD4rtException wrapper is
removed first, so this path agrees with the synchronous one, where
execute already rethrows the original value.
Setting a hook contains the error: it is reported here and not forwarded to the enclosing zone, which is what makes it usable as a sandbox boundary by a host that runs untrusted script.
Errors the caller can already observe are not routed here — anything that propagates through execute's return value or thrown exception stays on that path.
Set this before calling execute, and understand that setting it
makes d4rt own the error zone for the execution. That is what allows the
errors to be caught at all, but it also means a Future created by the
embedder before execute and passed into the script reports its errors
to the embedder's zone rather than to the script — the ordinary
consequence of an error-zone boundary, and the reason this is opt-in.
Leaving it null keeps the pre-existing routing untouched: escapes reach
the enclosing zone, still wrapped in the interpreter's internal exception
type.
Example:
final runner = D4rtRunner();
runner.onUncaughtError = (error, stackTrace) {
log.warning('script callback failed', error, stackTrace);
};
Implementation
void Function(Object error, StackTrace stackTrace)? onUncaughtError;