charmbracelet/crush · critical

failed to create lsp client: %w

Error message

failed to create lsp client: %w

What it means

Once command, args, and env are resolved, createPowernapClient builds a powernap.ClientConfig and calls powernap.NewClient. If that constructor fails, the error is wrapped as "failed to create lsp client". This typically means the underlying client could not be constructed (e.g. invalid combination of transport/command options) and the LSP server process was never started.

Source

Thrown at internal/lsp/client.go:223

	clientConfig := powernap.ClientConfig{
		Command:     home.Long(command),
		Args:        args,
		RootURI:     rootURI,
		Environment: envs,
		Settings:    c.config.Options,
		InitOptions: c.config.InitOptions,
		WorkspaceFolders: []protocol.WorkspaceFolder{
			{
				URI:  rootURI,
				Name: filepath.Base(c.cwd),
			},
		},
	}

	powernapClient, err := powernap.NewClient(clientConfig)
	if err != nil {
		return fmt.Errorf("failed to create lsp client: %w", err)
	}

	c.client = powernapClient
	return nil
}

// registerHandlers registers the standard LSP notification and request handlers.
func (c *Client) registerHandlers() {
	c.RegisterServerRequestHandler("workspace/applyEdit", HandleApplyEdit(c.client.GetOffsetEncoding()))
	c.RegisterServerRequestHandler("workspace/configuration", HandleWorkspaceConfiguration)
	c.RegisterServerRequestHandler("client/registerCapability", HandleRegisterCapability)
	c.RegisterServerRequestHandler("window/workDoneProgress/create", HandleWorkDoneProgressCreate)
	c.RegisterNotificationHandler("window/showMessage", func(ctx context.Context, method string, params json.RawMessage) {
		if c.debug {
			HandleServerMessage(ctx, method, params)
		}
	})
	c.RegisterNotificationHandler("textDocument/publishDiagnostics", func(_ context.Context, _ string, params json.RawMessage) {

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Verify the resolved LSP command exists and is executable: `which <command>` / `command -v <command>`.
  2. Install the LSP server or add its directory to PATH in the environment crush runs in.
  3. Check file permissions on the server binary (chmod +x).
  4. Run the command manually with the resolved args to confirm it can start.
  5. Confirm powernap client config fields (Command, Args, RootURI) are valid for the transport.

Example fix

// before
command = "gopls" // not installed

// after
// go install golang.org/x/tools/gopls@latest
command = "gopls"
Defensive patterns

Strategy: try-catch

Validate before calling

cmd, _ := resolver.ResolveValue(lspCfg.Command)
if _, err := exec.LookPath(cmd); err != nil {
    return fmt.Errorf("lsp server %q is not installed or not executable", cmd)
}

Type guard

func serverBinaryAvailable(cfg LSPConfig, r Resolver) bool {
    cmd, err := r.ResolveValue(cfg.Command)
    if err != nil {
        return false
    }
    _, err = exec.LookPath(cmd)
    return err == nil
}

Try / catch

client, err := lsp.New(cfg)
if err != nil {
    if strings.Contains(err.Error(), "failed to create lsp client") {
        slog.Error("Could not start LSP server; check it is installed and on PATH", "error", err)
        return nil // degrade gracefully; LSP features disabled
    }
    return err
}

Prevention

When it happens

Trigger: Calling New or Restart where powernap.NewClient(clientConfig) returns an error — e.g. the resolved command is not an executable binary on PATH, or the client config is structurally invalid for the transport.

Common situations: LSP server binary not installed or not on PATH in the runtime environment (works locally, fails in CI/container); command lacks execute permission; incompatible powernap client options after a version change.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/3e818488ee8942a1. Report an issue: GitHub.