Skip to content

LSP Shutdown Request Is Malformed #45994

@Spirrwell

Description

@Spirrwell

Reproduction steps

Assuming you have some kind of LSP enabled for whatever language you're working with, the repro steps are as simple as:

  1. Start Zed.
  2. Open project that will use your LSP.
  3. Close Zed (which should send a shutdown request to the LSP).
  4. View your Zed log (assuming your LSP gives a response error to malformed shutdown request)

I've noticed this issue specifically with the D language extension which uses the serve-d LSP. So, the below instructions will reflect that.

Rough Repro Steps:

  1. Download a D compiler and ensure it's on your PATH.
  2. Start Zed.
  3. Install/configure the D language extension. (I believe it has the ability to fetch the serve-d LSP on its own, though I have built serve-d from source and supplied the extension with a path to this)
  4. Create D project somewhere using a terminal using dub init . where you want to create the project. (dub is shipped with D compilers, think cargo-ish)
  5. Open this folder in Zed.
  6. Ensure serve-d is running. (Look at your Zed log or use your favorite task management program, top, ps, whatever)
  7. Close Zed.
  8. Note the output in your Zed log: 2026-01-03T11:57:58-05:00 ERROR [lsp] Shutdown request failure, server serve-d (id 3): params MUST be an object (named arguments) or array (positional arguments), other types are not allowed by spec

Current vs. Expected behavior

Current Behavior

The shutdown request sends a request where params is of unit type:

Image

(link to code:

pinned at specific commit in case of changes)

Which gets serialized here:

Image

(link to code:

params,
pinned at specific commit in case of changes)

As a result, the JSON received by the LSP will have params come in as null like: {"jsonrpc":"2.0","id":15,"method":"shutdown","params":null}

Which as I understand is incorrect for the JSON-RPC spec. See https://www.jsonrpc.org/specification (section 4) where it says

params
A Structured value that holds the parameter values to be used during the invocation of the method. This member MAY be omitted.

and

4.2 Parameter Structures
If present, parameters for the rpc call MUST be provided as a Structured value. Either by-position through an Array or by-name through an Object.

- by-position: params MUST be an Array, containing the values in the Server expected order.
- by-name: params MUST be an Object, with member names that match the Server expected parameter names. The absence of expected names MAY result in an error being generated. The names MUST match exactly, including case, to the method's expected parameters.

and if I pass in {"jsonrpc":"2.0","id":15,"method":"shutdown","params":null} to this validator https://www.json-rpc.dev/tools/validator it indicates that the message is invalid.

Expected Behavior

Per the JSON-RPC spec, the expected behavior is for params to be omitted if we're not sending params.

This could be done by modifying the Request struct to make params an Option type annotated with #[serde(default, skip_serializing_if = "Option::is_none")] like so:

Image

(Current code:

params: T,
pinned at specific commit in case of changes)

This would be a bit of an infectious change that'd require changing some function parameter types to Option<T::Params> and wrapping parameters passed in with Some(myParams) and passing None instead of () in the case of no parameters. But, it seems fairly trivial to do. If this is acceptable, I can create a Pull Request for this. (Though Rust definitely ain't my first language).

Zed version and system specs

Zed: Zed 0.217.3 80433cb
OS: Fedora Linux 43
Memory: 128 GB (acquired from before the RAM shortage :D)
Architecture: x86_64

It's worth noting that I also built Zed from main which has the same problem.

Attach Zed log file

Zed.log
2026-01-03T11:57:50-05:00 INFO  [zed] ========== starting zed version 0.217.3+stable.105.80433cb239e868271457ac376673a5f75bc4adb1, sha 80433cb ==========
2026-01-03T11:57:50-05:00 INFO  [crashes] spawning crash handler process
2026-01-03T11:57:50-05:00 INFO  [extension_host] extensions updated. loading 3, reloading 0, unloading 0
2026-01-03T11:57:50-05:00 INFO  [gpui::platform::linux::platform] activate is not implemented on Linux, ignoring the call
2026-01-03T11:57:50-05:00 INFO  [gpui::platform::blade::blade_renderer] Initializing Blade pipelines for surface SurfaceInfo { format: Bgra8Unorm, alpha: PreMultiplied }
2026-01-03T11:57:50-05:00 INFO  [workspace] Rendered first frame
2026-01-03T11:57:50-05:00 INFO  [zed::zed] Using GPU: GpuSpecs { is_software_emulated: false, device_name: "AMD Radeon RX 6600 XT (RADV NAVI23)", driver_name: "radv", driver_info: "Mesa 25.2.7" }
2026-01-03T11:57:50-05:00 INFO  [crashes] connected to crash handler process after 100ms
2026-01-03T11:57:50-05:00 INFO  [crashes] crash handler registered
2026-01-03T11:57:50-05:00 INFO  [util] set environment variables from shell:/bin/bash, path:/home/spirrwell/.cargo/bin:/home/spirrwell/.local/bin:/home/spirrwell/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin
2026-01-03T11:57:50-05:00 INFO  [node_runtime] Node runtime install_if_needed
2026-01-03T11:57:50-05:00 INFO  [language] found user-installed language server for clangd. path: "/usr/bin/clangd", arguments: []
2026-01-03T11:57:50-05:00 INFO  [lsp] starting language server process. binary path: "/usr/bin/clangd", working directory: "/home/spirrwell/Dev/dtest", args: []
2026-01-03T11:57:50-05:00 WARN  [language] unrecognized capture name 'test.inside' in D textobjects TreeSitter query
2026-01-03T11:57:50-05:00 WARN  [language] unrecognized capture name 'test.around' in D textobjects TreeSitter query
2026-01-03T11:57:50-05:00 WARN  [language] unrecognized capture name 'parameter.inside' in D textobjects TreeSitter query
2026-01-03T11:57:50-05:00 INFO  [lsp] starting language server process. binary path: "/home/spirrwell/.local/bin/serve-d", working directory: "/home/spirrwell/Dev/dtest", args: []
2026-01-03T11:57:50-05:00 INFO  [node_runtime] using Zed managed Node.js at /home/spirrwell/.local/share/zed/node/node-v24.11.0-linux-x64 since system Node.js wasn't found on PATH: cannot find binary path
2026-01-03T11:57:50-05:00 INFO  [lsp] Language server with id 3 sent unhandled notification coded/changedSelectedWorkspace:
{
  "uri": "file:///home/spirrwell/Dev/dtest",
  "name": "",
  "initialized": true,
  "selected": true,
  "pendingErrors": {}
}
2026-01-03T11:57:51-05:00 INFO  [lsp] Language server with id 3 sent unhandled notification coded/changedSelectedWorkspace:
{
  "uri": "file:///home/spirrwell/Dev/dtest",
  "name": "",
  "initialized": false,
  "selected": true,
  "pendingErrors": {}
}
2026-01-03T11:57:51-05:00 INFO  [lsp] Language server with id 3 sent unhandled notification coded/changedSelectedWorkspace:
{
  "uri": "file:///home/spirrwell/Dev/dtest",
  "name": "",
  "initialized": false,
  "selected": true,
  "pendingErrors": {}
}
2026-01-03T11:57:51-05:00 INFO  [lsp] starting language server process. binary path: "/home/spirrwell/.local/share/zed/node/node-v24.11.0-linux-x64/bin/node", working directory: "/home/spirrwell/Dev/dtest", args: ["/home/spirrwell/.local/share/zed/languages/json-language-server/node_modules/vscode-langservers-extracted/bin/vscode-json-language-server", "--stdio"]
2026-01-03T11:57:52-05:00 INFO  [lsp] Language server with id 3 sent unhandled notification coded/initDubTree:
null
2026-01-03T11:57:58-05:00 ERROR [crates/fs/src/fs_watcher.rs:216] Error { kind: WatchNotFound, paths: ["/home/spirrwell/.cache/zed/.tmpx8HEM3"] }
2026-01-03T11:57:58-05:00 ERROR [lsp] Shutdown request failure, server serve-d (id 3): `params` MUST be an object (named arguments) or array (positional arguments), other types are not allowed by spec: {"jsonrpc":"2.0","id":77,"method":"shutdown","params":null}

Relevant Zed settings

settings.json
{
  "completion_menu_scrollbar": "always",
  "auto_signature_help": true,
  "show_signature_help_after_edits": true,
  "auto_update": false,
  "lsp": {
    "serve-d": {
      "settings": {
        "d": {
          "enableFormatting": true,
          "enableDubLinting": true,
          "enableLinting": true,
          "manyProjectsThreshold": 20,
          "argumentSnippets": false,
          "scanAllFolders": false,
          "lintOnFileOpen": "project",
          "dubPath": "/home/spirrwell/Compilers/ldc2-1.41.0-linux-x86_64/bin/dub",
          "dubCompiler": "/home/spirrwell/Compilers/ldc2-1.41.0-linux-x86_64/bin/ldc2",
          "dcdClientPath": "/home/spirrwell/.local/bin/dcd-client",
          "dcdServerPath": "/home/spirrwell/.local/bin/dcd-server",
        },
        "dscanner": {
          "ignoredKeys": ["dscanner.style.long_line"],
        },
      },
      "binary": {
        "path": "/home/spirrwell/.local/bin/serve-d",
      },
    },
  },
}

Relevant Keymap

keymap.json
// Zed keymap
//
// For information on binding keys, see the Zed
// documentation: https://zed.dev/docs/key-bindings
//
// To see the default key bindings run `zed: open default keymap`
// from the command palette.
[
  {
    "context": "Workspace",
    "bindings": {
      // "shift shift": "file_finder::Toggle"
    }
  },
  {
    "context": "Editor && vim_mode == insert",
    "bindings": {
      // "j k": "vim::NormalBefore"
    }
  },
  {
    "context": "Pane",
    "bindings": {
      "alt-left": "pane::GoBack"
    }
  },
  {
    "context": "Pane",
    "bindings": {
      "alt-right": "pane::GoForward"
    }
  }
]

(for AI issues) Model provider details

No response

If you are using WSL on Windows, what flavor of Linux are you using?

None

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:language serverAn umbrella label for all language serversfrequency:alwaysBug that happens for everyone on every platform no matter how they use Zedpriority:P3Papercuts, minor issues with a clear workaround, cosmetic bugsstate:needs reproNeeds reproduction steps / someone to reproduce

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions