Describe the bug
When marimo convert privatizes a cell-level variable x to _x (because it is defined in multiple cells and would otherwise collide), it also renames every occurrence of x inside that cell — including references within nested def/lambda bodies whose own parameter is named x. The parameter is left as x, so the body now reads the cell-level _x closure instead of the argument.
This is silent when the enclosing cell defines _x: the function ignores its argument and reads the closure, so it runs without error and produces a wrong result (no crash, and marimo check reports nothing). When the enclosing cell does not define _x, it instead becomes a NameError / undefined-name at runtime.
Root problem: a function/lambda parameter introduces a new scope that shadows the cell-level name, so occurrences of that name inside the function body should not be rewritten by the cell-level rename.
Expected: the nested function's parameter and its uses stay consistent — either the parameter is renamed too, or (correct) the body is left alone because the parameter shadows the cell-level variable.
Actual: body uses are renamed to _x, parameter stays x → function silently ignores its argument.
Reproduction
bug.ipynb — two code cells (n appears in both cells, so convert privatizes it):
Cell 1:
def double(n):
return n * 2
n = 5
print('double(3) =', double(3)) # correct: 6
Cell 2:
Run:
marimo convert bug.ipynb -o bug.py
Produced bug.py (note def double(n) but body return _n * 2):
import marimo
__generated_with = "0.24.0"
app = marimo.App()
@app.cell
def _():
def double(n):
return _n * 2
_n = 5
print('double(3) =', double(3))
return
@app.cell
def _():
_n = 999
print('n =', _n)
return
if __name__ == "__main__":
app.run()
double(3) now returns 10 (_n * 2 = 5 * 2) instead of 6. The function silently ignores its argument. This also affects lambda parameters, comprehension/for targets, and with ... as targets that share a name with a privatized cell-level variable.
Will you submit a PR?
Environment
{
"marimo": "0.24.0",
"editable": false,
"OS": "Linux",
"OS Version": "6.17.0-1032-oem",
"Processor": "x86_64",
"Python Version": "3.12.13",
"Locale": "en_US",
"Binaries": {
"Browser": "--",
"Node": "v18.19.1",
"uv": "0.10.8"
},
"Dependencies": {
"click": "8.4.2",
"docutils": "0.23",
"itsdangerous": "2.2.0",
"jedi": "0.19.2",
"markdown": "3.10.3",
"narwhals": "2.24.0",
"packaging": "26.2",
"psutil": "7.2.2",
"pygments": "2.20.0",
"pymdown-extensions": "11.0.2",
"pyyaml": "6.0.3",
"starlette": "1.2.1",
"tomlkit": "0.15.1",
"typing-extensions": "4.16.0",
"uvicorn": "0.52.0",
"websockets": "16.1.1"
},
"Optional Dependencies": {
"loro": "1.13.2",
"openai": "2.50.0",
"pandas": "3.0.5",
"pytest": "9.1.1",
"ruff": "0.16.0"
},
"Experimental Flags": {}
}
Code to reproduce
No response
Describe the bug
When
marimo convertprivatizes a cell-level variablexto_x(because it is defined in multiple cells and would otherwise collide), it also renames every occurrence ofxinside that cell — including references within nesteddef/lambdabodies whose own parameter is namedx. The parameter is left asx, so the body now reads the cell-level_xclosure instead of the argument.This is silent when the enclosing cell defines
_x: the function ignores its argument and reads the closure, so it runs without error and produces a wrong result (no crash, andmarimo checkreports nothing). When the enclosing cell does not define_x, it instead becomes aNameError/ undefined-name at runtime.Root problem: a function/lambda parameter introduces a new scope that shadows the cell-level name, so occurrences of that name inside the function body should not be rewritten by the cell-level rename.
Expected: the nested function's parameter and its uses stay consistent — either the parameter is renamed too, or (correct) the body is left alone because the parameter shadows the cell-level variable.
Actual: body uses are renamed to
_x, parameter staysx→ function silently ignores its argument.Reproduction
bug.ipynb— two code cells (nappears in both cells, so convert privatizes it):Cell 1:
Cell 2:
Run:
Produced
bug.py(notedef double(n)but bodyreturn _n * 2):double(3)now returns 10 (_n * 2=5 * 2) instead of 6. The function silently ignores its argument. This also affectslambdaparameters, comprehension/fortargets, andwith ... astargets that share a name with a privatized cell-level variable.Will you submit a PR?
Environment
{ "marimo": "0.24.0", "editable": false, "OS": "Linux", "OS Version": "6.17.0-1032-oem", "Processor": "x86_64", "Python Version": "3.12.13", "Locale": "en_US", "Binaries": { "Browser": "--", "Node": "v18.19.1", "uv": "0.10.8" }, "Dependencies": { "click": "8.4.2", "docutils": "0.23", "itsdangerous": "2.2.0", "jedi": "0.19.2", "markdown": "3.10.3", "narwhals": "2.24.0", "packaging": "26.2", "psutil": "7.2.2", "pygments": "2.20.0", "pymdown-extensions": "11.0.2", "pyyaml": "6.0.3", "starlette": "1.2.1", "tomlkit": "0.15.1", "typing-extensions": "4.16.0", "uvicorn": "0.52.0", "websockets": "16.1.1" }, "Optional Dependencies": { "loro": "1.13.2", "openai": "2.50.0", "pandas": "3.0.5", "pytest": "9.1.1", "ruff": "0.16.0" }, "Experimental Flags": {} }Code to reproduce
No response