Skip to content

No Python kernel offered when the interpreter lives outside the workspace (dev container); only "marimo sandbox" is listed #819

Description

@rigarash

Summary

In a Dev Container whose Python environment is a venv outside the workspace
(/opt/venv, baked into the image), the notebook kernel picker never offers
that interpreter. Depending on timing it offers either the system interpreter
(/usr/bin/python3, which has no marimo) or nothing at all — leaving
marimo sandbox as the only selectable kernel.

Selecting the correct interpreter through Python: Select Interpreter — where
it is listed and selectable — does not change the kernel list.

Environment

vscode-marimo 0.17.2 (linux-x64)
VS Code (server) 1.135.0 stable, commit 08d4889f
ms-python.python 2026.4.0
ms-python.vscode-python-envs 1.36.0
Remote Dev Containers over Remote-SSH (compose-based)
Container OS Ubuntu 22.04.3 LTS
marimo (in the venv) 0.24.0

Relevant settings (dev container machine settings):

"python.defaultInterpreterPath": "/opt/venv/bin/python",
"python.venvPath": "/opt",
"python-envs.globalSearchPaths": ["/opt"],
"marimo.disableUvIntegration": true

The environment itself is a normal venv and is fully usable:

$ /opt/venv/bin/python -c "import sys, marimo; print(sys.prefix, marimo.__version__)"
/opt/venv 0.24.0
$ cat /opt/venv/pyvenv.cfg
home = /usr/bin
version_info = 3.10.12
include-system-site-packages = false
prompt = example

Note that /usr/bin/python3 and /opt/venv/bin/python report the same
version
(3.10.12); only the latter has marimo, pandas, etc.

Steps to reproduce

  1. Build a container image whose Python environment is a venv outside the
    workspace folder (here /opt/venv; the workspace has no .venv).
  2. Point the Python extensions at it with the three settings above.
  3. Open a .py marimo notebook and open the kernel picker.

Expected

A controller for /opt/venv/bin/python is offered (it is a known environment —
see below), so the notebook can run in the image's environment.

Actual

Action Kernel list
After container rebuild Python 3.10.12 (= /usr/bin/python3, no marimo) + marimo sandbox
After Developer: Reload Window marimo sandbox only
After Python: Select Interpreter/opt/venv/bin/python unchanged

Developer: Restart Extension Host reconnects the remote and reproduces the
same race, so it is not a workaround.

The environment is definitely discovered by the Python tooling. From
Python Environments.log:

Discovered env: /opt/venv/bin/python
Discovered env: /usr/bin/python
Found venv environment: example (3.10.12)
Environment discovery complete: 2 environments found (Global: 1, venv: 1)

and it is persisted in the locator cache:

{"environment": {"executable": "/opt/venv/bin/python", "prefix": "/opt/venv",
                 "version": "3.10.12.final.0"}}

It is also listed and selectable in Python: Select Interpreter.

What the bundled code appears to do

Reading dist/extension.js of 0.17.2, NotebookControllers.refresh runs in
exactly two places:

const a = p.fn("NotebookControllers.refresh")(function* () {
  const c = yield* r.knownEnvironments;
  const l = c.filter(u => !rcn(u, { code: e, uvCacheDir: i }));
  ...
});
yield* a();                                                   // once, at activation
yield* p.forkScoped(r.environmentChanges.pipe(V.runForEach(a)));  // onDidChangeEnvironments

with

knownEnvironments: p.sync(() => e.environments.known)

Two consequences seem to follow:

  1. The list is a snapshot taken at activation. The extension activates on
    onStartupFinished (and declares extensionDependencies: ["ms-python.python"]),
    which can be well before environment discovery finishes. In this container the
    search path covers ~6.6 GB (/opt/nvidia 1.5 GB + /opt/venv 5.1 GB), and
    discovery completes ~20 s after startup.

  2. Selecting an interpreter does not refresh the controllers.
    activeEnvironmentPathChanges is subscribed elsewhere (a debounced
    PythonEnvInvalidation), but NotebookControllers.refresh is not wired to it,
    so Python: Select Interpreter has no effect on the kernel list.

The filter itself is not the cause — rcn only excludes environments inside the
uv cache directory
:

function rcn(t, e) {
  if (C.isNone(e.uvCacheDir)) return !1;
  try { return ncn(e.code.Uri.file(t.path).fsPath, e.uvCacheDir.value.fsPath) }
  catch { return !1 }
}

/opt/venv is not under the uv cache, so it is not being filtered out — it seems
to simply be absent from the snapshot.

That the system interpreter appears after a rebuild but disappears after a reload
also suggests the contents of the snapshot depend on timing rather than on any
property of the environments.

Suggested fix

Re-run NotebookControllers.refresh when the active environment changes
(environments.onDidChangeActiveEnvironmentPath), in addition to
onDidChangeEnvironments. That alone would give users a reliable manual remedy —
pick the interpreter, get the kernel — regardless of how discovery is timed.

Optionally: await environments.refreshEnvironments() before the first refresh,
and/or expose a "marimo: Refresh kernels" command.

Two smaller things noticed while investigating

  1. The kernel label can be misleading. The label is built as
    name ? "<name> (Python <ver>)" : "Python <ver>" where name comes from
    environment.name / the basename of environment.folderUri. An environment
    resolved via python.defaultInterpreterPath has no environment, so it shows
    as a bare Python <ver> — indistinguishable from the system interpreter when
    both report the same version. (controller.description carries the path, which
    helps, but it was not visible in the picker in our case.)

  2. The sandbox kernel rewrites the notebook file. Selecting marimo sandbox
    makes marimo insert PEP 723 metadata into the notebook
    (marimo/_cli/sandbox.py::_ensure_marimo_in_script_metadata runs
    uv add --script <file> marimo). For projects whose dependencies are locked
    elsewhere this is an unwanted edit, and it re-appears on every run — it is not
    obvious from the UI that choosing a kernel will modify the file on disk. A
    confirmation, or a setting to disable it, would help.
    (marimo.disableUvIntegration: true does not prevent this.)

Workaround we settled on

Run the notebooks outside the extension:

marimo edit --headless --host 127.0.0.1 --port 2718 notebooks/
marimo export html -o out.html notebooks/reports/<name>.py
python notebooks/reports/<name>.py

All three work correctly in the same container, with the same interpreter.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions