gopls - The Go Language Server

How the official Go language server powers autocompletion, diagnostics, and refactoring in any LSP-compatible editor

When you open a Go file in your editor, a separate program called gopls starts running in the background. It reads your code, understands the structure of your project, and answers questions from the editor in real time — things like “what methods does this type have?” or “where is this function defined?”. gopls (pronounced “Go please”) is the official Go language server, built by the Go team to replace the older generation of standalone tools like gocode, godef, and goimports that editors used to stitch together.

What a Language Server Actually Solves

Before the Language Server Protocol (LSP), every editor had to write its own Go integration from scratch. That meant Vim plugins shipped one implementation of autocomplete, VS Code shipped another, and Emacs shipped a third. When the Go team improved tooling, each plugin had to be updated independently. Bugs got fixed in one editor and ignored in another.

LSP solves this by defining a single protocol between editors and language servers. The editor acts as a client that sends requests — “give me completions for this cursor position” — and the server responds with structured data. The editor only needs to implement the LSP client side once, and after that it can support any language that has a server. gopls is that server for Go. It handles all the language intelligence, and your editor just needs to know how to display the results.

Not just Go:

The LSP approach is used across many languages: rust-analyzer for Rust, pyright for Python, typescript-language-server for TypeScript. Once you understand the model, it transfers across ecosystems.

This architecture means gopls runs as a standalone process that communicates with the editor over JSON-RPC. You do not interact with it directly in normal use — your editor plugin starts it, sends queries, and displays the answers. You might never see gopls appear on your screen, but it is doing the heavy lifting behind every code suggestion, red squiggly line, and refactoring operation.

Installing gopls

Most editors handle installation automatically. When you install the official Go extension for VS Code or open a .go file with a configured Vim plugin, the editor will download and install the latest stable gopls for you. This is the recommended path.

If you need to install it manually — for a CI pipeline, a custom editor setup, or to pin a specific version — use go install:

go install golang.org/x/tools/gopls@latest

This downloads and compiles the gopls binary into your $GOPATH/bin directory (by default $HOME/go/bin). You must ensure that directory is on your system PATH so your editor can locate the binary.

PATH matters for manual installs:

If you install gopls manually, run which gopls in a terminal to confirm it is accessible. Many editor startup issues trace back to a missing $GOPATH/bin entry in the shell profile that launched the editor.

To verify the installation and see which version you have:

gopls version

A successful output looks something like golang.org/x/tools/gopls v0.16.0. That confirms gopls is built from a known release and ready to serve.

How gopls Works with Your Code

When gopls starts, it loads your workspace. The workspace defines the boundaries of what the server “sees” — which packages are part of the project and how they relate to each other. This is not just a formality; everything from autocomplete to code navigation depends on it.

Module Mode and GOPATH Mode

gopls supports two workspace layouts: Go modules (the default since Go 1.16) and the legacy GOPATH mode. The server detects which mode to use based on the presence of a go.mod file:

  • If it finds a go.mod at the root of your workspace, it treats the directory as a module and respects module boundaries. Imports are resolved using the module’s require directives and the module cache.
  • If there is no go.mod, gopls falls back to GOPATH mode and expects your code to sit inside $GOPATH/src following the older directory convention.

Multi-module workspaces — where you have several go.mod files in subdirectories — are also supported. You can open a parent directory containing multiple modules, and gopls will understand each as a separate module.

Workspace root matters:

Opening a file in isolation without its module root can break gopls. If you see “no required module provides package” errors, you likely opened a directory above or below the actual module root. Open the directory containing go.mod as your workspace root.

Three Go Versions to Keep Track Of

gopls interacts with Go at three different version levels, and understanding this distinction prevents confusion when something behaves unexpectedly:

  1. The Go version used to build gopls itself. As of gopls v0.17.0, you need Go 1.21 or later to build the server. But thanks to forward compatibility introduced in Go 1.21, the go install command will automatically download a newer toolchain if your local version is too old — you do not need to manually upgrade Go just to build gopls.
  2. The Go version found on $PATH when gopls runs. This is the go command that gopls invokes internally to load package metadata. It must be one of the two most recent Go releases, per the Go release policy. If it is older, gopls may refuse to function or produce incomplete results.
  3. The Go version declared in go.mod of the project you are editing. This determines which language features and standard library APIs gopls considers available. Code written for Go 1.22 won’t get completions from packages introduced in 1.23, and gopls will correctly flag language features that are too new for the declared version.

Older Go on PATH breaks gopls:

A common scenario: your project uses Go 1.21, but your system go command is stuck on 1.19. gopls will run, but diagnostics may be wrong and some features will silently disappear. Always run go version to confirm the active toolchain is recent enough.

Core IDE Features gopls Provides

gopls implements a wide range of LSP features. The list below covers what you will use every day.

Autocompletion

As you type, gopls suggests identifiers — variables, functions, packages, methods — that are valid at the cursor position. It ranks suggestions by relevance, filters by type compatibility where possible, and even proposes entire snippets for common patterns like error handling or loop constructs.

The completion is context-aware. If you type fmt.Pr, it completes to fmt.Println but also offers fmt.Printf and fmt.Print. If you are inside a method call on an io.Reader, it will only suggest methods available on that interface.

A lesser-known behavior: when you autocomplete a function call that returns multiple values, gopls can fill in the if err != nil block automatically if the return includes an error. This is controlled by the ui.completion.usePlaceholders setting.

Diagnostics

Squiggly underlines that appear as you write are diagnostics from gopls. Unlike a full go build, which might be slow for large codebases, gopls provides near-instant feedback by compiling only the changed files incrementally. This catches syntax errors, type mismatches, unused variables, and even stylistic issues like missing documentation comments on exported identifiers.

The diagnostics respect the Go version in your go.mod. If you declare go 1.20 but use cmp.Or (added in Go 1.22), gopls will flag it as undefined — even if your system go command is newer.

  • Go to Definition: Jump from a function call to its declaration, even across packages. Hover over an identifier to see its type signature and documentation.
  • Find References: List every location where a function, variable, or type is used throughout the workspace.
  • Workspace Symbols: Search for any exported identifier across your entire project by name — no need to know which file it lives in.
  • Call Hierarchy: Visualize which functions call a given function and which functions it calls. Useful for tracing execution paths in unfamiliar code.

Refactoring

gopls supports several code transformations that preserve behavior while restructuring code:

  • Rename: Change an identifier everywhere it appears, including in other packages and test files, with full semantic awareness. Renaming a method on an exported interface will update all implementations automatically.
  • Extract Variable / Extract Function: Select an expression and turn it into a named variable or a standalone function. gopls computes the correct type and determines which variables need to be passed as parameters.
  • Fill Struct: Given a struct literal, generate all missing field names and zero values so you do not have to type them manually.

Code Generation

Write a type and let gopls generate common boilerplate. The two most frequent cases are generating interface implementations (stubbing out all required methods) and generating getter/setter methods for struct fields. This works across the workspace: if an interface from a library requires ten methods, gopls can create all ten in your type with one action.

Try it yourself:

Type var _ io.Reader = (*MyType)(nil) in a file and then trigger the “implement interface” command. gopls will generate all the necessary method stubs for MyType. This is a fast way to bootstrap conforming to an interface without manually looking up its documentation.

Configuring gopls

You do not configure gopls through a configuration file in your project. Instead, settings are passed from the editor client. Most editors expose these settings in their own configuration format, and they map directly to the gopls settings documented in the official settings page.

In VS Code, open settings.json and add entries under the "gopls" key:

{
  "gopls": {
    "ui.completion.usePlaceholders": true,
    "ui.diagnostic.staticcheck": true,
    "formatting.gofumpt": true
  }
}

Some commonly adjusted settings include:

  • ui.completion.usePlaceholders: When true, function parameter placeholders appear in completions so you can tab through them.
  • formatting.gofumpt: Enforces a stricter formatting style than gofmt. gopls will format your code on save accordingly.
  • analyses: Controls which static analysis checks run. The default set includes unusedparams, shadow, and nilness. You can add fieldalignment or simplifycompositelit for more aggressive feedback.

Restart after changes:

After changing a gopls setting, you may need to restart the language server for it to take effect. Each editor has its own command for this — typically searching “restart language server” in the command palette works.

When gopls Does Not Work as Expected

Even with a properly installed server, issues can arise. The most common ones stem from workspace misconfiguration or environment mismatches.

“No required module provides package”

This error means gopls cannot find the module that owns a package you imported. The most frequent cause: you opened a directory that is not the module root (the folder containing go.mod). Close the workspace and reopen it at the module root.

gopls starts but diagnostics never appear

This often means gopls is using an older version of the go command that does not understand newer module syntax. Run go version inside your project to confirm it matches a supported Go release. If your editor uses a different $PATH than your terminal, that mismatch can be hard to spot — some editors let you explicitly set the Go binary path in their settings.

Completions are slow or hang on large codebases

Large monorepos with many modules can stress gopls. A practical mitigation is to open only the specific module you are working on, rather than the entire repository root. If you must work with multiple modules, use a go.work file (Go workspace mode) to declare the set of modules you actively develop. gopls will limit its scope to those modules.

Bazel and non-standard build systems

gopls is designed to work with the go command toolchain. If your project builds with Bazel or another system, gopls will not understand the project layout out of the box. Some teams have created go/packages drivers that bridge Bazel to gopls, but this is not officially supported and requires careful setup. If you are working in such an environment, expect a higher configuration burden.

Bazel support is unofficial:

If your team uses Bazel, do not assume gopls will work correctly without explicit configuration. The integration is documented in bazelbuild/rules_go#512, but many features remain fragile. Test thoroughly before adopting it as your daily driver.

The gopls CLI — When You Need to See Under the Hood

Though you typically interact with gopls through an editor, the server ships with a command-line interface that is invaluable for debugging. The gopls binary itself accepts several subcommands.

To see all available checks and their status in your workspace:

gopls check .

This runs the same diagnostics that your editor displays, printed directly to the terminal. It is useful in CI pipelines to verify that gopls diagnostics are clean without opening an editor.

Another common command inspects the structure of your workspace as gopls sees it:

gopls workspace .

This outputs the module layout, the Go version for each module, and any environment variables gopls is using. When the editor shows a confusing error, running this command can reveal that gopls is looking at a different directory or a stale go.sum than you expected.

For a full list of available commands and their flags, run gopls help. The serve subcommand starts the language server in LSP mode directly — you will rarely need this unless you are writing a new editor plugin.

Summary

gopls is the engine that turns any LSP-compatible editor into a capable Go IDE. It bundles autocompletion, diagnostics, navigation, and refactoring into a single process, replacing the fragmented tooling of earlier years. Most of the time, it works without you ever thinking about it — your editor plugin handles installation, startup, and configuration.

The two things that will break gopls are workspace misalignment and an unsupported Go toolchain on PATH. When something goes wrong, the fix is almost always to open the correct module root and ensure go version returns a recent release. For deeper investigation, the gopls check and gopls workspace commands expose exactly what the server sees.