Course · OpenCode
Troubleshooting
Each section below is a self-contained diagnosis for a failure OpenCode users commonly hit: the conditions to check, ordered so the most likely cause goes first, and the command or config change that resolves it. Behavior is described as of October 2026; every remedy links the documentation page it rests on, or the OpenCode source file where the documentation is silent.
LSP not starting
Section titled “LSP not starting”LSP (Language Server Protocol) integration is disabled by default, so if the diagnostics (the compiler errors a language server reports back to the agent) never arrive, first confirm "lsp": true is set in your opencode.json, at the project root or globally at ~/.config/opencode/opencode.json. Then check the language server itself. For Go, the documented requirement is only that the go command is available (LSP documentation): if gopls is not on your PATH, OpenCode runs go install golang.org/x/tools/gopls@latest into its own bin directory (as of October 2026; Go server source). So check three things: which go finds the Go toolchain, the OPENCODE_DISABLE_LSP_DOWNLOAD environment variable is not set to true (that setting turns the install off), and the machine can reach the network for go install. If you install gopls yourself and put it on your PATH, OpenCode uses that binary and skips the download. For other languages, the LSP documentation’s server table lists each server’s requirement.
MCP server won’t connect
Section titled “MCP server won’t connect”For a local server, run the server command standalone in your terminal first (e.g. DATABASE_URI=... uvx postgres-mcp --access-mode=restricted) and read stderr - if the server cannot start on its own, OpenCode cannot talk to it. Then verify the declared type matches where the server actually runs: an MCP (Model Context Protocol) server declared "type": "local" is a command that OpenCode starts itself, while "type": "remote" means OpenCode connects to a server at a URL. A local command declared remote, or a URL endpoint declared local, cannot connect. Finally, check that the binary or runtime the command needs (uvx, npx, node) is on your PATH inside the environment OpenCode runs in - a service manager or container often has a sparser PATH than your interactive shell (MCP documentation). For a remote server, run opencode mcp list to see each server’s status and connection error, opencode mcp auth <name> to rerun OAuth, and opencode mcp debug <name> to test HTTP connectivity and the OAuth flow (MCP documentation).
/undo doesn’t revert files
Section titled “/undo doesn’t revert files”/undo removes the most recent user message, its responses, and the file changes that came with them - and two conditions must hold for the file part to work. First, the project must be a Git repository: the rollback manages file changes through Git, and outside a repository there is nothing to roll back through (TUI documentation). Second, snapshots must be on: the rollback rides OpenCode’s snapshot system, which is enabled by default and tracks file changes in an internal git repository (snapshot source), but if it was turned off ("snapshot": false in opencode.json) the agent’s changes cannot be rolled back through the UI (config documentation). If snapshots are off, or the result is unclear, use Git as the authoritative checkpoint: git diff to see what changed, then git stash or another deliberate recovery step. Commit early and often so you always have a clean checkpoint to return to.
Compaction loses critical context
Section titled “Compaction loses critical context”Compaction rewrites a long session onto a summary when the context window fills (automatically by default, or on demand with /compact), and detail that lived only in the thread can vanish with it. Write key decisions and findings down before that happens, and note what brings each one back into context. OpenCode includes AGENTS.md, the project instructions file, in the model’s context on every request (rules documentation), so a rule there is still present after compaction. A DECISIONS.md or a comment in the code is on disk but not in context: the agent sees it again only when AGENTS.md points to it or you ask the agent to read it after compaction. Use the documented compaction settings to tune the mechanism: compaction.auto (default true) triggers the summarization, compaction.prune (default false, an opt-in) additionally strips old tool output to reclaim space, and compaction.reserved tunes the token buffer kept in reserve (config documentation; unset, OpenCode reserves the smaller of 20,000 tokens and the model’s output limit, per the session source). Anything that exists only in the conversation is at risk when the window compacts.
Headless run stops at an ask permission
Section titled “Headless run stops at an ask permission”In a run with no human at the keyboard, an ask rule (a permission setting in opencode.json that pauses a tool until someone approves it) has no one to answer it. opencode run, on its own or with --attach pointed at a remote server, does not wait: without --auto, it rejects each request that would ask and prints a permission requested: ...; auto-rejecting warning. By default a rejected permission also stops the agent’s loop, so the run ends early, and a job that looks complete can be half done (as of October 2026; run.ts and processor.ts source).
For headless and CI use, the safer default is a purpose-built agent with a deliberate permission map - every key an explicit allow or deny, never a dangling ask. Define it under agent in opencode.json with its own permission block, and select it with opencode run --agent <name> (agents documentation); if the name is not found or the agent has "mode": "subagent", the run prints a warning and falls back to the default agent (run.ts). The documented escape hatch for unattended runs is the --auto flag: launched with it, OpenCode automatically approves permission requests that are not explicitly denied - explicit deny rules are still enforced, and auto mode only changes requests that would otherwise ask for approval (permissions documentation). Unlike a rejected ask, a deny does not end the run: the model gets an error for that call and continues (as of October 2026; processor.ts stops the loop only on a rejection). Auto mode is a loosening reached by a flag, not a new safety layer. A config with no ask rules of its own can still hit this: the defaults ask for doom_loop, external_directory, and, in the source, reads of .env files (agent.ts; the permissions documentation says those reads are denied). Because the source default is ask, --auto approves .env reads, so before you use it add an explicit deny: "read": { "*.env": "deny", "*.env.*": "deny", "*.env.example": "allow" }.
OpenCode runs as a server, and the TUI, opencode run, and your own programs are its clients (server documentation), so an unanswered request plays out differently per client. A program that drives an opencode serve server through its API and never answers the permission request leaves the tool call pending, because the server waits for a reply with no timeout (permission source). The standalone opencode attach <url> command is the interactive counterpart: it attaches a terminal to a running server, where someone can answer (CLI documentation documents all three commands).
Port 4096 already in use
Section titled “Port 4096 already in use”opencode serve listens on port 4096 at hostname 127.0.0.1 by default (server documentation), so the usual cause is another opencode serve process already holding the port. An interactive TUI (terminal user interface) session does not usually hold 4096: when you start the TUI, it assigns its own server a random port unless you pass --port. Find the process with lsof -i :4096 or ss -tlnp | grep 4096, stop it, or start your server on a different port with the --port flag: opencode serve --port 4097.