Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anyway to have the complete stack trace of the NoCanvasContext exception ? #783

Open
stephaneeybert opened this issue Apr 25, 2020 · 7 comments
Labels

Comments

@stephaneeybert
Copy link

stephaneeybert commented Apr 25, 2020

I sometimes get the error RuntimeError {code: "NoCanvasContext", message: "Can't draw without a canvas context."} and I'm having a hard time to pin point my source code that triggers it. Indeed, it's not giving me a complete stack trace at all.

The trouble is that it completely stops the rendering from happening afterwards. And it even stops the application from working properly.

For now, I catch it before it bubbles up and makes too much trouble:

        try {
          const sheetStaveNoteGroup: any = sheetContext.openGroup();
          this.styleStaveNote(placedChord, VEXFLOW_NOTE_HIGHLIGHT_COLOR)
          .draw();
          sheetContext.closeGroup();
          placedChord.sheetStaveNoteHighlightGroup = sheetStaveNoteGroup;
        } catch (error) {
          this.reloadPage();
        }
@sschmidTU
Copy link
Contributor

sschmidTU commented Apr 30, 2020

It seems the context of a StaveNote is null (not set).

If you search for this message in the Vexflow code, you'll see it only occurs in stavenote.js.
There are a few functions this can occur in, like drawLedgerLines, drawFlag, drawModifiers.

If you want to find out which, edit the messages to be unique for each function, and compile your modified Vexflow (or simply replace this string in vexflow-debug.js if you use that).

Otherwise, you could simply try to check each stavenote's stavenote.context before drawing it and check/debug when it is null:

if (!stavenote.context) {
  console.log("problem here");
}

@stephaneeybert
Copy link
Author

But why is the API swallowing the exceptions ?

For example, today, I'm having to pinpoint the origin of the following error:

error: RuntimeError {code: "BadArguments", message: "The provided duration is not valid: p"}
id: "My API id--1590165133370"
message: RuntimeError {code: "BadArguments", message: "The provided duration is not valid: p"}
method: null
name: null
stack: RuntimeError
code: "BadArguments"
message: "The provided duration is not valid: p"

And the stack has been swallowed again. Too bad.

Please let the smell bubble up :-)

@sschmidTU
Copy link
Contributor

sschmidTU commented May 22, 2020

This happens when you add a note, for example:

var note = vf.StaveNote({ keys: ['b/4'], duration: '1r', align_center: true }); // whole rest

var voice = vf.Voice()
  .setStrict(false)
  .addTickables([note]);

if you search for the error message in the vexflow code, you can trace it to the note constructor, and further to tables.js:

Flow.durationAliases = {
  'w': '1',
  'h': '2',
  'q': '4',
  'b': '256',
};

'p' doesn't exist as a duration alias. maybe you meant to use 'r' here?
To trace this, i'd debug your code and set a breakpoint when you include 'p' in the duration string.

But i generally agree the code error smell should bubble up ;)

@0xfe
Copy link
Owner

0xfe commented May 22, 2020

Thanks folks -- I do want to fix this and make error handling and exceptions more consistent, and welcome ideas here. The primary constraint is that we can't break existing users, who have (unfortunately) adapted to some of these anti-patterns.

@stephaneeybert
Copy link
Author

@sschmidTU Thanks for the effort ! I had quickly found the issue. I was reporting here the missing stack. This is no show stopper of course.

For fun reading, may I share with you all some writing of mine regarding exceptions ? It's a small subset of some Java language exception text I had written a long time ago. But it gives a picture.

There you are:

An exception is not an error code, but rather a mechanism designed to ease error handling, without having to deal with the error until it gets to its dedicated exception handler, often away from the origin of the error.

Implement the exception handler where it belongs to, that is, in the component that actually knows what to do with the error, and let the exception freely bubble up to the exception handler.

Have few centralised exception handlers, and no error handling code anywhere else in the application. Exceptions must travel freely through the call stack, up to the exception handler.

Exceptions must keep the complete information. Never discard the stack trace and never replace it with a custom error message.

The component is a good place to have the exception handler, as it has all the context to know what to do with the exception. It catches all bubbled-up exceptions, it can handle some of them based on some business cases of the application, and redirect the rest of them to a system error page, displaying just a user friendly message.

The exception handler can also be in a service. For example, if the service has enough knowledge to interpret a specific error, then it's okay for the service to catch that exception. The service must then wrap it into a more meaningful exception with a readable message. But the service cannot recover from the exception. It can only help clarify the error. The new exception must contain the nested original exception, and it must reach the exception handler in the component. The user is not interested in what exactly happened, if there is no business case for that condition. The user must simply be redirected to an error page and the exception logged. The complete stack trace with all the additional information supplied by the service that wrapped the original exception can then be logged.

@ronyeh
Copy link
Collaborator

ronyeh commented Oct 1, 2021

This is a topic that I have thought about. Glad to run across a related issue!

If you are using a release build, what I would do is add a

debugger;

statement right before the exception you are seeing.

Just open up the vexflow-debug.js in your sublime or vscode or vim and search for the applicable lines and drop in the debugger; statement.

Then open the JS dev tools of your favorite browser. When the exception is about to hit, it should run into your debugger statement and show you the complete stack trace.

In a future PR, I plan to stick a debugger statement into our runtime exception utility method.

Maybe we can have webpack strip the debugger statement and (maybe all exception handling) from the release builds. Only developers should worry about exceptions and stack traces. The release builds probably don't need these types of checks.

@ronyeh ronyeh added the 4.x label Oct 1, 2021
@rvilarl
Copy link
Collaborator

rvilarl commented Jan 1, 2023

@ronyeh are you planning that for 4.x?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants