jeapostrophe / racket-langserver

Other
262 stars 24 forks source link

Exception on stdout causing jsonrpc parse error #85

Closed elmisback closed 1 year ago

elmisback commented 1 year ago

tldr: Is there a way to have the language server not put errors into stdout/avoid exception printing when invoking the language server?

Hi, I'm a Magic Racket user (without much racket background, so there might be an easy fix) who is trying to figure out where a bug is coming from. I posted an issue in Magic Racket (https://github.com/Eugleo/magic-racket/issues/77#issuecomment-1301553286). It seems like stdout+stderr are getting mixed by the language server, and Magic Racket is unable to handle the error because of an uncatchable underlying exception in the jsonrpc parsing library (https://github.com/microsoft/vscode-languageserver-node/issues/870).

I have a program that looks like this:

#lang racket

(require plot/no-gui)
(local-require (only-in plot/no-gui [plot-file plot-file*]))

This leads to a message from the language server with a mixture of racket error and jsonrpc data, which I caught in the VSCode extension debugger:

Caught exn:
standard-module-name-resolver: contract violation
  expected: module-path?
  given: '(just-space #f (rename plot/no-gui b plot-file))
  context...:
   /home/elm/.local/share/racket/8.6/pkgs/racket-langserver/autocomplete.rkt:106:0: walk-module
   /home/elm/.local/share/racket/8.6/pkgs/racket-langserver/check-syntax.rkt:111:10
   /usr/share/racket/collects/racket/logging.rkt:43:0: with-intercepted-logging
   /home/elm/.local/share/racket/8.6/pkgs/racket-langserver/check-syntax.rkt:77:0: check-syntax
   /usr/share/racket/collects/racket/contract/private/arrow-val-first.rkt:486:18
   /home/elm/.local/share/racket/8.6/pkgs/racket-langserver/text-document.rkt:65:0: did-open!
   /usr/share/racket/collects/racket/contract/private/arrow-val-first.rkt:486:18
   [repeats 1 more time]
   /home/elm/.local/share/racket/8.6/pkgs/racket-langserver/main.rkt:54:2: consume

Caught exn in request "textDocument/documentSymbol"
hash-ref: no value found for key
  key: 'file:///home/elm/temp/tst.rkt
  context...:
   /home/elm/.local/share/racket/8.6/pkgs/racket-langserver/text-document.rkt:405:0: document-symbol
   /usr/share/racket/collects/racket/contract/private/arrow-val-first.rkt:486:18
   /home/elm/.local/share/racket/8.6/pkgs/racket-langserver/methods.rkt:26:0: process-message
   /usr/share/racket/collects/racket/contract/private/arrow-val-first.rkt:486:18
   /home/elm/.local/share/racket/8.6/pkgs/racket-langserver/main.rkt:54:2: consume

Content-Length: 117

{"error":{"code":-32603,"message":"internal error in method \"textDocument/documentSymbol\""},"id":1,"jsonrpc":"2.0"}

jsonrpc fails to find the Content-Length header in this buffer due to the error text, and after the first such error, all subsequent requests result in the same problem since there's extra stuff in the buffer and it's never cleared. So it seems like this is a problem with stdout getting mixed with stderr.

I tried setting -W0 etc. in the langserver args to turn off stderr messages, but stuff still came through. I also tried redirection using a shell with 2>/dev/null and executing the langserver after loading an initial file that sets current-error-port to nowhere:

const executable = {  // VSCode language client config
      command: '/bin/bash',
      args: ['xvfb-run', '--auto-servernum', 'racket','-f', '/home/elm/nostderr.rkt', '--lib', 'racket-langserver', '2>/dev/null' ],
      // options: {
      //   shell: true
      // }
    };

with nostderr.rkt:

#lang racket
(require (only-in racket/port open-output-nowhere))
(current-error-port (open-output-nowhere))

Those didn't work either. My understanding of how the VSCode language server actually runs the process based on the executable configuration above and how to configure ports in racket are weak, so there's probably something here that I'm missing. I was able to solve the issue by commenting out the relevant eprintf lines in a racket-langserver fork.

So basically it seems like what's needed is an option to turn off the error printing to the extent that it's possible, since Magic Racket doesn't have a way to gracefully handle printed errors until the PR for the issue I linked goes through (no activity since December 2021).

6cdh commented 1 year ago

It's not related to stdout/stderr. That's an exception that happened in the didOpen method and didn't put it into the opened document hash table. Any other later requests will produce a hash-ref error.

A simple fix is to change local-require to require. Or waiting for it fixed by someone who really understands what happened :).

elmisback commented 1 year ago

Thank you for having a look at this issue! While you might be right about the didOpen issue, my problem was actually because xvfb-run dumps stderr into stdout (!), so JSON-RPC was trying to read the report-error output. https://bugs.launchpad.net/ubuntu/+source/xorg-server/+bug/1059947. I confirmed in a debugger that the mixed stream caused the JSON-RPC parser to miss the content-length header and then lose its place forever.

I fixed this by modifying xvfb-run as suggested here: https://superuser.com/questions/1719253/xvfb-run-redirects-stdout-and-stderr-to-stdout-only. You may want to suggest this for other users who resort to using xvfb-run for running headless as mentioned in (https://github.com/jeapostrophe/racket-langserver/issues/45).