mathjax / MathJax-node

MathJax for Node
Apache License 2.0
614 stars 97 forks source link

Uncaught [TypeError: Cannot read property 'setAttribute' of undefined] #441

Open dac514 opened 5 years ago

dac514 commented 5 years ago

I'm working on an express.js app that wraps this library and returns a PNG image. Sometimes the equations will be less than stellar, possibly garbage. This example crashes my node app, leaves it in an usable state, I have to restart the server:

Equation:

\begin{equation*} A = \left[ \begin{array}{cccc} a_{11} & a_{12} & a_{13} & a_{14} \\ a_{21} & a_{22} & a_{23} & a_{24} \\ a_{31} & a_{32} & a_{33} & a_{34} \end{array} \right] \end{equation*}

Error:

Math Processing Error: Cannot read property 'toFixed' of undefined                                                                                                      
Error: TypeError: Cannot read property 'setAttribute' of undefined                                                                                                      
    at GetSVG (C:\Users\dac\Code\github\pressbooks\pb-mathjax\node_modules\mathjax-node\lib\main.js:698:7)                                                              
    at Function.execute (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:243:26)                                        
    at cb (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:225:59)                                                      
    at Object.Process (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:495:38)                                          
    at Object.call (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:508:37)                                             
    at Function.WAITEXECUTE (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:348:50)                                    
    at cb (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:225:59)                                                      
    at Object.Process (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:495:38)                                          
    at Object.call (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:508:37)                                             
    at Function.WAITEXECUTE (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:348:50)                                    
Error: Uncaught [TypeError: Cannot read property 'setAttribute' of undefined]                                                                                           
    at reportException (C:\Users\dac\Code\github\pressbooks\pb-mathjax\node_modules\jsdom\lib\jsdom\living\helpers\runtime-script-errors.js:66:24)                      
    at processJavaScript (C:\Users\dac\Code\github\pressbooks\pb-mathjax\node_modules\jsdom\lib\jsdom\living\nodes\HTMLScriptElement-impl.js:192:7)                     
    at HTMLScriptElementImpl._innerEval (C:\Users\dac\Code\github\pressbooks\pb-mathjax\node_modules\jsdom\lib\jsdom\living\nodes\HTMLScriptElement-impl.js:122:5)      
    at C:\Users\dac\Code\github\pressbooks\pb-mathjax\node_modules\jsdom\lib\jsdom\browser\resource-loader.js:31:22                                                     
    at Object.check (C:\Users\dac\Code\github\pressbooks\pb-mathjax\node_modules\jsdom\lib\jsdom\living\nodes\Document-impl.js:76:11)                                   
    at C:\Users\dac\Code\github\pressbooks\pb-mathjax\node_modules\jsdom\lib\jsdom\living\nodes\Document-impl.js:95:12                                                  
    at wrappedEnqueued (C:\Users\dac\Code\github\pressbooks\pb-mathjax\node_modules\jsdom\lib\jsdom\browser\resource-loader.js:253:16)                                  
    at ReadStream.readableStream.on (C:\Users\dac\Code\github\pressbooks\pb-mathjax\node_modules\jsdom\lib\jsdom\browser\resource-loader.js:74:7)                       
    at ReadStream.emit (events.js:203:15)                                                                                                                               
    at endReadableNT (_stream_readable.js:1129:12) TypeError: Cannot read property 'setAttribute' of undefined                                                          
    at GetSVG (C:\Users\dac\Code\github\pressbooks\pb-mathjax\node_modules\mathjax-node\lib\main.js:698:7)                                                              
    at Function.execute (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:243:26)                                        
    at cb (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:225:59)                                                      
    at Object.Process (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:495:38)                                          
    at Object.call (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:508:37)                                             
    at Function.WAITEXECUTE (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:348:50)                                    
    at cb (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:225:59)                                                      
    at Object.Process (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:495:38)                                          
    at Object.call (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:508:37)                                             
    at Function.WAITEXECUTE (file:///C:/Users/dac/Code/github/pressbooks/pb-mathjax/node_modules/mathjax/unpacked/MathJax.js:348:50)                                    

Configs:

{
      MathJax: {
        extensions: ['Safe.js'],
        displayMessages: false,
        displayErrors: false,
        AsciiMath: {
          // @see http://docs.mathjax.org/en/latest/asciimath.html
        },
        SVG: {
          blacker: 0,
        },
      },
    }
{
      math: myEquation,
      format: 'AsciiMath',
      svg: true,
      speakText: true, // a11y
    }

I've tried putting the code in a try/catch block but it doesn't do anything.

Help?

dpvc commented 5 years ago

The main issue is that you have specified that your expression is an AsciiMath one, but really it is a TeX one. It appears that AsciiMath produces bad MathML when given this TeX expression.

If you change

format: 'AsciiMath'

to

format: 'TeX'

it should work for you.

An additional issue is that you have included displayMesages and displayErrors in the MathJax configuration block, but they should be outside that in the main configuration object:

    {
      displayMessages: false,
      displayErrors: false,
      MathJax: {
        extensions: ['Safe.js'],
        AsciiMath: {
          // @see http://docs.mathjax.org/en/latest/asciimath.html
        },
        SVG: {
          blacker: 0,
        },
      },
    }

But setting the wrong format is the one that is causing you the errors above.

dac514 commented 5 years ago

Thank you for your reply, but:

Sometimes the equations will be less than stellar, possibly garbage.

My main concern is that I need to be able to recover from this error.

Similar errors (bad MathML, bad TeX) throw errors I can catch.

This one puts me in a weird limbo state. My app crashes and I can't do anything. Express.js simply hangs forever. I cannot do try/catch, I cannot do then().catch() and I can't seem to do process.on('unhandledRejection') or process.on('uncaughtException')

Here's the app in question: https://github.com/pressbooks/pb-mathjax

Here's a URL that crashes it:

http://localhost:3000/asciimath?asciimath=%5Cbegin%7Bequation%2A%7D%20%20A%20%3D%20%5Cleft%5B%20%5Cbegin%7Barray%7D%7Bcccc%7D%20%20a_%7B11%7D%20%26%20a_%7B12%7D%20%26%20a_%7B13%7D%20%26%20a_%7B14%7D%20%5C%5C%20%20a_%7B21%7D%20%26%20a_%7B22%7D%20%26%20a_%7B23%7D%20%26%20a_%7B24%7D%20%5C%5C%20%20a_%7B31%7D%20%26%20a_%7B32%7D%20%26%20a_%7B33%7D%20%26%20a_%7B34%7D%20%20%5Cend%7Barray%7D%20%5Cright%5D%20%20%5Cend%7Bequation%2A%7D&fg=561442

If I pass the same "junk" to MathML:

http://localhost:3000/mathml?mathml=%5Cbegin%7Bequation%2A%7D%20%20A%20%3D%20%5Cleft%5B%20%5Cbegin%7Barray%7D%7Bcccc%7D%20%20a_%7B11%7D%20%26%20a_%7B12%7D%20%26%20a_%7B13%7D%20%26%20a_%7B14%7D%20%5C%5C%20%20a_%7B21%7D%20%26%20a_%7B22%7D%20%26%20a_%7B23%7D%20%26%20a_%7B24%7D%20%5C%5C%20%20a_%7B31%7D%20%26%20a_%7B32%7D%20%26%20a_%7B33%7D%20%26%20a_%7B34%7D%20%20%5Cend%7Barray%7D%20%5Cright%5D%20%20%5Cend%7Bequation%2A%7D&fg=561442

It complains just as much, but I can catch the error and do something about it.

Ideas?

pkra commented 5 years ago

This is, ultimately, an upstream issue in MathJax and asciimath. There should be a better error handling on MathJax's end and, ultimately, asciimath should produce some reasonable output for this (bad) input.

However, since your project is in its early stages (great to see PB making this btw!), you might consider jumping ahead to MathJax v3.

pkra commented 5 years ago

I've filed https://github.com/asciimath/asciimathml/issues/106 and https://github.com/mathjax/MathJax/issues/2153 -- reduced test case: {cc}.

dac514 commented 5 years ago

However, since your project is in its early stages (great to see PB making this btw!), you might consider jumping ahead to MathJax v3.

Thanks for the reply and upstream reports!

I looked at mj3-demos-node and see: tex2svg and mml2svg examples in: https://github.com/mathjax/mj3-demos-node/tree/master/component but I don't see asciimath2svg. Docs and release notes for the latest beta say "Other input and output processors (e.g., AsciiMath input) will be added in the future." Is this still the case or is there some development branch I can point to? Supporting AsciiMath is a feature we'd like to promote when this is released.

For now, for this particular issue, I've set a timer that exits the script if (I think that) something crashed. Works like:

  // Consider an init longer than 5 seconds a crash and exit
  const tooLong = setTimeout(() => {
    // @see https://github.com/mathjax/MathJax-node/issues/441
    console.error('Too long, Something crashed? Please restart the server.');
    process.exit(1);
  }, 5000);
  mjAPI.config(configs.mathjax);
  mjAPI.typeset(configs.typeset).then((data) => {
    clearTimeout(tooLong);
    // ... snip ...
  }).catch((err) => {
    clearTimeout(tooLong);
    // ... snip ...
  });

Node severs like pm2 and forever will restart automatically when this happens, good enough for now.

dpvc commented 5 years ago

Sometimes the equations will be less than stellar, possibly garbage.

Sorry, I thought you meant the resulting output was bad, not the input provided to MathJax. I understand what you re saying now.

My main concern is that I need to be able to recover from this error.

It turns out that mathjax-node doesn't handle the case where the SVG wasn't created (in this case, due to AsciiMath crashing on the bad input). Because MathJax is running inside a jsdom virtual DOM inside mathjax-node, your try/catch are unable to trap the error.

If you add

if (!svg) return;

in between lines 697 and 698

https://github.com/mathjax/MathJax-node/blob/c7e5bf3baf4af820e68be58cb93ade38b8e19cbe/lib/main.js#L697-L698

in node_modules/mathjax-node/lib/main.js, that will prevent this crash, and your mjAPI.typeset() call will pass on the error so that your catch will run with err being the array of errors that caused the problem. In this case, it will be a Math Processing Error since the AsciiMath input jax died.


... I don't see asciimath2svg ... is there some development branch I can point to?

The AsciiMath input jax has not yet been ported to v3, but there is a legacy v2 AsciiMath input jax that is patched in for testing purposes. To use it, you would need to use the "direct" method of loading MathJax, rather than the components (since no AsciiMath components are included), or you could build your own component for AsciiMath (starting with the MathML input component as a template, it should not be too hard). The legacy AsciiMath input jax is asynchronous, so you would have to use the promise-based conversion features; but you are already working with promises, so that should not be a problem.

It would take a little more work to get AsciiMath working in v3 than to make the one-line fix to mathjax-node that I give above, but I suspect it will be worth it, as it will be both faster, more stable, and more maintainable in the long run.

dpvc commented 5 years ago

I was just trying out an AsciiMath component for MathJax v3, and realized that because of the legacy v2 code, you can't webpack it. If your app requires the code to be webpacked, then you won't be able to use AsciiMath until it is properly ported to v3.

dpvc commented 5 years ago

OK, it is possible to make a webpackable asciimath component by changing

https://github.com/mathjax/mathjax-v3/blob/660ad53770f47915f6e99abf62299e7da343fa5e/mathjax2/legacy/MathJax.js#L776

to

timeout();

and

https://github.com/mathjax/mathjax-v3/blob/660ad53770f47915f6e99abf62299e7da343fa5e/mathjax2/input/AsciiMath.js#L1

to

MathJax = Object.assign(global.MathJax || {}, require("../legacy/MathJax.js").MathJax);

Then the component can be modeled on components/src/input/mathml and basically just change mml to asciimath and MathML to AsciiMath throughout. The entities subdirectory can be removed.

dpvc commented 5 years ago

I've made a asciimath-component branch of the v3 code that includes the webpackable AsciiMath component.

dac514 commented 5 years ago

Update: I'm going to hold off on MathJax3. The syntax & convention changes from this project to that one aren't trivial for me. I spent an hour trying to wedge it into pb-mathjax and failed.

Would love https://github.com/mathjax/MathJax-node/pull/442 integrated though.

Regards,