Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Language server (tsc --lsp) exits with code 1 after `shutdown` and `exit`

Open Beginner friendly
#64,742 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
76/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
go

Research direction

Start in tsc/internal/lsp/server.go, following the issue’s handleExit, dispatchLoop, and Run path; then read tsc/cmd/tsc/lsp.go where the error becomes exit code 1. Reproduce with the supplied repro.py both with and without an opened document. Done means a clean shutdown followed by exit returns code 0, while the issue’s stated unexpected-exit behavior remains intact.

Written by the indexing model from the issue text.

Description

🔎 Search Terms

tsc --lsp exit code 1, shutdown exit, context canceled, handleExit io.EOF, LSP exit notification

🕗 Version & Regression Information
  • I was unable to test this on prior versions because --lsp is new in TypeScript 7: seen with typescript 7.0.2 (tsc --lsp --stdio), macOS arm64. I have not run the nightly; the code path below is unchanged on main at aa81492.
⏯ Playground Link

No response (language server, not type checking)

💻 Code

Save as repro.py and run python3 repro.py in any directory. It initializes tsc --lsp --stdio, sends shutdown, then exit, and prints the exit code (given a URI, it opens a document first; no argument opens nothing).

# python3 repro.py [URI to open], in a directory with a tsconfig.json
import json, os, subprocess, sys

p = subprocess.Popen(["tsc", "--lsp", "--stdio"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

def send(msg):
    body = json.dumps({"jsonrpc": "2.0", **msg}).encode()
    p.stdin.write(b"Content-Length: %d\r\n\r\n%s" % (len(body), body))
    p.stdin.flush()

def reply(id):
    while True:
        length = None
        while (line := p.stdout.readline()) not in (b"\r\n", b""):
            if line.lower().startswith(b"content-length:"):
                length = int(line.split(b":")[1])
        if length is None:
            return None  # the server is gone
        msg = json.loads(p.stdout.read(length))
        if "method" in msg and "id" in msg:  # a request from the server
            send({"id": msg["id"], "result": None})
        elif msg.get("id") == id:
            return msg

root = "file://" + os.getcwd()
send({"id": 1, "method": "initialize", "params": {"processId": os.getpid(), "rootUri": root, "capabilities": {}}})
reply(1)
send({"method": "initialized", "params": {}})
if len(sys.argv) > 1:
    doc = {"uri": sys.argv[1], "languageId": "typescript", "version": 0, "text": "const x = 1;\n"}
    send({"method": "textDocument/didOpen", "params": {"textDocument": doc}})
send({"id": 2, "method": "shutdown"})
if reply(2) is not None:
    send({"method": "exit"})
print("exit code:", p.wait())
🙁 Actual behavior
$ python3 repro.py
context canceled
exit code: 1

The server exits with code 1 after a clean shutdown and exit. Neovim reports any non-zero exit, so every :lsp restart shows "Client tsc quit with exit code 1 and signal 0. Check log for errors".

Reading main at aa81492, the path looks like this:

  • handleExit returns io.EOF.
  • dispatchLoop answers that with lspExit(nil), which cancels the loop's context.
  • Its next s.requestQueue.Get(ctx) then returns context.Canceled, and the loop returns that.
  • Run passes it on, since it isn't io.EOF and the group's context is done.
  • cmd/tsc/lsp.go prints it and returns 1.
🙂 Expected behavior

Exit code 0: the LSP spec asks for 0 when shutdown came before exit, and 1 otherwise.

Additional information about the issue

No response

Dominant language
Go
Stars
111k
Forks
16.4k
Avg merge
1d 11h
Merged PRs (30d)
148

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from microsoft/TypeScript

All issues in microsoft/TypeScript

Similar issues

More Go issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.