Summary
When a ManimWeb scene throws at runtime (inside the async player.sequence(...) callback), the error only appears in the browser devtools console as an uncaught promise rejection. It never reaches the widget's error traitlet, so:
- nothing is shown in the notebook cell output (the widget just renders blank/partial), and
- it can't be read back from Python (
widget.error stays ""), which is exactly what a coding agent pairing in the notebook needs.
The error trait exists and is synced, but it seems to only capture engine/CDN-load failures, not errors thrown while executing the user scene code.
Concrete example
from wigglystuff import ManimWeb
# Text takes an options object; passing a positional string is a mistake,
# but the failure mode is what matters here.
scene = '''
const player = new manim.Player(container, { width, height, autoPlay: true, backgroundColor: manim.WHITE });
player.sequence(async (scene) => {
const title = new manim.Text("hello", { fontSize: 26 }); // wrong: text must be in options
await scene.play(new manim.Write(title));
});
'''
w = ManimWeb(code=scene, width=640, height=380)
w
Browser console shows:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'split')
at e._measureText (manim-web.browser.js:...)
at e._renderToCanvas (...)
at new e (...) // new manim.Text(...)
at WB.sequence (...)
But w.error == "" in Python, and the cell shows no error.
Expected
Runtime errors from the scene (including async rejections inside player.sequence) should be caught and written to the error traitlet, and rendered in the widget's error area (there's already a manim-web-error element/class).
Suggested fix
In frame/scene runner in static/manim-web.js, wrap the executed scene function and attach handlers so both sync throws and async rejections propagate to model:
try {
await runUserScene(...); // the async function built from `code`
} catch (err) {
model.set("error", String(err && err.stack || err));
model.save_changes();
// also render into the .manim-web-error element
}
// belt-and-suspenders for stray rejections:
window.addEventListener("unhandledrejection", (e) => {
model.set("error", String(e.reason && e.reason.stack || e.reason));
model.save_changes();
});
This makes scene bugs visible in the notebook itself and readable via widget.error — a big help when iterating on scenes (and essential for agents driving the notebook, who can't see the browser console).
Environment
- wigglystuff 0.5.16 (ManimWeb), marimo notebook on a remote MoLab kernel
- manim-web 0.3.24
Summary
When a
ManimWebscene throws at runtime (inside the asyncplayer.sequence(...)callback), the error only appears in the browser devtools console as an uncaught promise rejection. It never reaches the widget'serrortraitlet, so:widget.errorstays""), which is exactly what a coding agent pairing in the notebook needs.The
errortrait exists and is synced, but it seems to only capture engine/CDN-load failures, not errors thrown while executing the user scene code.Concrete example
Browser console shows:
But
w.error == ""in Python, and the cell shows no error.Expected
Runtime errors from the scene (including async rejections inside
player.sequence) should be caught and written to theerrortraitlet, and rendered in the widget's error area (there's already amanim-web-errorelement/class).Suggested fix
In
frame/scene runner instatic/manim-web.js, wrap the executed scene function and attach handlers so both sync throws and async rejections propagate tomodel:This makes scene bugs visible in the notebook itself and readable via
widget.error— a big help when iterating on scenes (and essential for agents driving the notebook, who can't see the browser console).Environment