Skip to content

ManimWeb: scene runtime errors don't reach the error traitlet (invisible in notebook / to Python) #291

Description

@koaning

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions