Bug Description
Pyright LSP fails to resolve packages from virtual environments in monorepo setups, even when a .venv symlink exists at the project root. The LSP reports Import "X" could not be resolved for all third-party packages.
Environment
- OpenCode version: Latest (Dec 25, 2025)
- OS: macOS (Darwin)
- Python: 3.12
- Pyright version: 1.1.407
Reproduction Steps
- Have a monorepo with a Python project in a subdirectory (e.g.,
apps/api/)
- Virtual environment is at
apps/api/.venv
- Create a symlink at project root:
ln -s apps/api/.venv .venv
- Have a
pyrightconfig.json at project root
- Run OpenCode and open a Python file
- LSP diagnostics show import errors for installed packages
Expected Behavior
Pyright LSP should resolve packages from the virtual environment, similar to how the CLI works:
# CLI pyright works correctly:
$ .venv/bin/pyright apps/api/app/main.py
# Only shows 3 real errors, no import errors
# But LSP in OpenCode shows:
# error[Pyright] (reportMissingImports): Import "fastapi" could not be resolved
# error[Pyright] (reportMissingImports): Import "uvicorn" could not be resolved
Root Cause Analysis
After analyzing the source code, I identified the issue in packages/opencode/src/lsp/client.ts:
Current Implementation (lines 67-70):
connection.onRequest("workspace/configuration", async () => {
// Return server initialization options
return [input.server.initialization ?? {}]
})
The Problem
-
Ignores request parameters: The handler ignores the ConfigurationParams which contains items specifying which configuration sections are being requested.
-
Pyright requests multiple sections: Pyright makes separate configuration requests for:
python section (for pythonPath, venvPath)
python.analysis section (for stubPath, typeshedPaths, etc.)
-
Same response for all requests: OpenCode returns the same flat object regardless of which section is requested, which breaks pyright's expectation of getting section-specific configuration.
How Pyright Uses Configuration (from pyright's server.ts):
// Pyright requests the 'python' section
const pythonSection = await this.getConfiguration(workspace.rootUri, 'python');
if (pythonSection) {
const pythonPath = pythonSection.pythonPath;
// ...
const venvPath = pythonSection.venvPath;
// ...
}
// Pyright requests the 'python.analysis' section separately
const pythonAnalysisSection = await this.getConfiguration(workspace.rootUri, 'python.analysis');
// ...
OpenCode's Pyright Initialization (server.ts lines 523-537):
const initialization: Record<string, string> = {}
const potentialVenvPaths = [process.env["VIRTUAL_ENV"], path.join(root, ".venv"), path.join(root, "venv")]
for (const venvPath of potentialVenvPaths) {
const potentialPythonPath = path.join(venvPath, "bin", "python")
if (await Bun.file(potentialPythonPath).exists()) {
initialization["pythonPath"] = potentialPythonPath // Sets flat key
break
}
}
This sets pythonPath correctly, but pyright also needs venvPath to be set for proper package resolution without running the interpreter.
Suggested Fix
Option 1: Properly handle configuration sections
connection.onRequest("workspace/configuration", async (params: ConfigurationParams) => {
const results: any[] = [];
for (const item of params.items) {
if (item.section === 'python') {
results.push({
pythonPath: input.server.initialization?.pythonPath,
venvPath: input.server.initialization?.venvPath,
});
} else if (item.section === 'python.analysis') {
results.push(input.server.initialization?.pythonAnalysis ?? {});
} else {
results.push(input.server.initialization ?? {});
}
}
return results;
})
Option 2: Add venvPath to initialization
In server.ts Pyright spawn function:
const potentialVenvPaths = [process.env["VIRTUAL_ENV"], path.join(root, ".venv"), path.join(root, "venv")]
for (const venvPath of potentialVenvPaths) {
const potentialPythonPath = path.join(venvPath, "bin", "python")
if (await Bun.file(potentialPythonPath).exists()) {
initialization["pythonPath"] = potentialPythonPath
initialization["venvPath"] = path.dirname(venvPath) // Add parent directory
initialization["venv"] = path.basename(venvPath) // Add venv folder name
break
}
}
Additional Context
- The CLI
pyright command works correctly because it reads pyrightconfig.json
- The pyright-langserver should also read
pyrightconfig.json, but the LSP settings seem to override or interfere with it
- Setting
pythonPath alone requires pyright to execute the Python interpreter to discover sys.path, which may fail in certain environments
- Setting
venvPath + venv directly tells pyright where to find site-packages without needing to run Python
Workaround
Currently, no reliable workaround exists within OpenCode. Users must rely on pyrightconfig.json being read correctly, which doesn't appear to work with the LSP integration.
Bug Description
Pyright LSP fails to resolve packages from virtual environments in monorepo setups, even when a
.venvsymlink exists at the project root. The LSP reportsImport "X" could not be resolvedfor all third-party packages.Environment
Reproduction Steps
apps/api/)apps/api/.venvln -s apps/api/.venv .venvpyrightconfig.jsonat project rootExpected Behavior
Pyright LSP should resolve packages from the virtual environment, similar to how the CLI works:
Root Cause Analysis
After analyzing the source code, I identified the issue in
packages/opencode/src/lsp/client.ts:Current Implementation (lines 67-70):
The Problem
Ignores request parameters: The handler ignores the
ConfigurationParamswhich containsitemsspecifying which configuration sections are being requested.Pyright requests multiple sections: Pyright makes separate configuration requests for:
pythonsection (forpythonPath,venvPath)python.analysissection (forstubPath,typeshedPaths, etc.)Same response for all requests: OpenCode returns the same flat object regardless of which section is requested, which breaks pyright's expectation of getting section-specific configuration.
How Pyright Uses Configuration (from pyright's server.ts):
OpenCode's Pyright Initialization (server.ts lines 523-537):
This sets
pythonPathcorrectly, but pyright also needsvenvPathto be set for proper package resolution without running the interpreter.Suggested Fix
Option 1: Properly handle configuration sections
Option 2: Add venvPath to initialization
In
server.tsPyright spawn function:Additional Context
pyrightcommand works correctly because it readspyrightconfig.jsonpyrightconfig.json, but the LSP settings seem to override or interfere with itpythonPathalone requires pyright to execute the Python interpreter to discoversys.path, which may fail in certain environmentsvenvPath+venvdirectly tells pyright where to find site-packages without needing to run PythonWorkaround
Currently, no reliable workaround exists within OpenCode. Users must rely on
pyrightconfig.jsonbeing read correctly, which doesn't appear to work with the LSP integration.