Problem
In a harness where a main agent and several subagents each have an eval / persistent-kernel tool, the kernel namespace can be shared across all of them. During a phase with 5 concurrent agents this bit twice in one session:
- A subagent had a global named
rowsoverwritten by a peer mid-run. - The main agent bound
p = pathlib.Path(...)in one cell and usedp.write_text(...)in a later cell. Between the two cells a peer reboundpto a string. The failure surfaced as:
AttributeError: 'str' object has no attribute 'write_text'which reads like a type bug in your own code and sends you re-reading logic that is correct.
Why it's hard to spot
- The prelude/docs say state persists across cells, which is true -- it just doesn't say whose state.
- Short, natural names (
p,rows,data,df,res) are exactly what independent agents pick independently, so collision probability is high precisely when concurrency is high. - The error is a plain
AttributeError/TypeErrorat the point of use, arbitrarily far from the clobber. Nothing points at concurrency. - It's nondeterministic: rerun the cell alone and it works.
Mitigations
- Prefix globals per agent/task during any parallel phase:
_dr_lp,_dr_rowsinstead ofp,rows. - Re-bind in the same cell that uses it. Treat cross-cell globals as unsafe while siblings are live -- cheap to re-derive a
Pathor re-read a small file. - Prefer passing state through files (
local://, artifact paths) rather than kernel globals when agents run concurrently. - If you get an inexplicable type error on a name you're sure you bound correctly,
print(type(name))first and suspect a peer before suspecting your logic.
Generalizes to
Any shared long-lived interpreter session: Jupyter kernels driven by multiple clients, a shared tmux REPL, MCP servers exposing one Python session to several callers, notebook-backed CI runners.