"Cannot serialize function: type not supported"

Hello,

I’m trying to organize my LSP configurations in a “smarter” way to avoid repeating myself over and over again. Here’s an except of what I did.

----------------------
-- Language servers --
----------------------
local servers                         = {
    bashls    = {}, -- bash
    clangd    = {}, -- C/CPP
    cssls     = {}, -- CSS
    html      = {}, -- HTML
    jsonls    = {}, -- JSON
...
}

Foreach(servers,
    function(k, v)
        require "lspconfig"[k].setup {
            capabilities = capabilities,
            on_attach    = function(client, bufno)
                on_attach(client, bufno);
                (v.on_attach or function(...) end)(client, bufno)
            end,
            settings     = v,
            filetypes    = v.filetypes,
        }
    end)

I expect to be able to call the on_attach function shared globally that invokes LSP functionalities, as well as override those bindings with some language specific bindings (called right after the global one). This however doesn’t work and shows the error “Cannot serialize function: type not supported”. What could be causing this, and how could I fix this?

Thank you for your time :slight_smile:

The exact cause is impossible to tell without more context. In general it means you are trying to pass a function across languages, such as passing a Lua function to an API function. The problem is that a “function” cannot be represented outside the runtime it is defined in. A number is a number, the exact byte pattern, precision or range might vary, but the general concept is universal. On the other hand, a function depends on the environment it is defined in. Even if you could serialize the source code of the function, without access to the lexical environment it was defined in the source code would be insufficient to reconstruct the behaviour of the function on the other side.

Had literally the same issue configuring the LSPs, and I resolved it by not propagating the settings key value as is, but rather settings a key value in the server config instead. Here’s the code that I have

local servers = {
  clangd = {},
  gopls = {},
  pyright = {},
  rust_analyzer = {},
  denols = {
    root_dir = require("lspconfig.util").root_pattern("deno.json", "deno.jsonc")
  },
  tsserver = {
    single_file_support = false,
    root_dir = require("lspconfig.util").root_pattern("package.json")
  },
  eslint = { filetypes = { 'javascript', 'typescript', 'typescriptreact' } },
  html = { filetypes = { 'html', 'twig', 'hbs' } },
  lua_ls = {
    -- See here the `settings` key
    settings = {
      Lua = {
        workspace = { checkThirdParty = false },
        telemetry = { enable = false },
      },
    },
  },
}

-- Other stuff ...

mason_lspconfig.setup_handlers {
  function(server_name)
    require('lspconfig')[server_name].setup {
      capabilities = capabilities,
      on_attach = on_attach,
      single_file_support = (servers[server_name] or {}).single_file_support,
      root_dir = (servers[server_name] or {}).root_dir,
      -- See here, as I was passing the whole config before
      settings = (servers[server_name] or {}).settings,
      filetypes = (servers[server_name] or {}).filetypes,
    }
  end,
}

I think it might be something related to when the settings are set internally, and maybe it messes up something when passing a function that does requires, but I’m no expert in Lua tbh.

Hope it helps.

1 Like