Problem
Python edits to a file seeded with vm.filesystem.writeFile() can succeed and be visible inside the Python execution, then silently revert to the API-seeded bytes when the host reads the file after execution.
This is the Python VFS bridge counterpart of #1961. The JavaScript fix in #1981 is present in the tested source, but Python uses a different write handler. Related: the shell/WASM report #1901 is still open. This report is specifically about Python writes, not the shell-to-Python child-process issue #2012.
Reproduction
With a VM in which Python execution is available:
await vm.filesystem.mkdir('/workspace', { recursive: true });
await vm.filesystem.writeFile(
'/workspace/data.txt', new TextEncoder().encode('original\n'),
);
const result = await vm.python.execute(`
from pathlib import Path
p = Path('/workspace/data.txt')
p.write_text('EDITED\\n')
print(p.read_text(), end='')
`, { output: { capture: 'all' } });
console.log(result.stdout); // EDITED\n
console.log(new TextDecoder().decode(
await vm.filesystem.readFile('/workspace/data.txt'),
)); // original\n -- unexpected
Expected: both reads return EDITED\n. Actual: Python sees its edit and exits successfully, but the following filesystem API read returns original\n; subsequent executions also see the reverted data. A read-modify-write (p.write_text(p.read_text() + 'added\\n')) has the same problem. Seeding a fresh file through Python rather than through the filesystem API avoided the stale API-created shadow in our tests.
Verification
- Reproduced with the published 0.2.22 packages on Linux arm64, including embedded
AgentOs.create() without HTTP, Rivet actors, or PostgreSQL. That setup used a command-registration workaround to enable Python; this report starts after Python executes successfully.
- Independently reproduced against unpatched upstream source at
c3698e6a60be0a6fed61d411779930e0a7753fc8 using the native-sidecar Rust wire harness and ExecuteRequest { runtime: Python, ... }. This bypasses SDK interpreter-command registration entirely.
- The new regression fails with
left: "original\n", right: "EDITED\n" on its first post-execution host read. The bridge bundle was built from that checkout; no application storage service is needed.
Suspected cause
The wire WriteFile handler mirrors bytes into the VM's root staging/shadow tree. service_owned_python_filesystem_rpc_request handles Python Write by updating only the kernel VFS. Subsequent shadow-to-kernel reconciliation imports the unchanged staging copy and overwrites the newer Python bytes. The JavaScript mirroring change in #1981 does not run on this Python RPC path.
I am preparing a focused fix and regressions for Python writes, including read-modify-write and atomic replacement. Python append-position/truncate behavior and shell/WASM shadow writes are separate from this fix.
Problem
Python edits to a file seeded with
vm.filesystem.writeFile()can succeed and be visible inside the Python execution, then silently revert to the API-seeded bytes when the host reads the file after execution.This is the Python VFS bridge counterpart of #1961. The JavaScript fix in #1981 is present in the tested source, but Python uses a different write handler. Related: the shell/WASM report #1901 is still open. This report is specifically about Python writes, not the shell-to-Python child-process issue #2012.
Reproduction
With a VM in which Python execution is available:
Expected: both reads return
EDITED\n. Actual: Python sees its edit and exits successfully, but the following filesystem API read returnsoriginal\n; subsequent executions also see the reverted data. A read-modify-write (p.write_text(p.read_text() + 'added\\n')) has the same problem. Seeding a fresh file through Python rather than through the filesystem API avoided the stale API-created shadow in our tests.Verification
AgentOs.create()without HTTP, Rivet actors, or PostgreSQL. That setup used a command-registration workaround to enable Python; this report starts after Python executes successfully.c3698e6a60be0a6fed61d411779930e0a7753fc8using the native-sidecar Rust wire harness andExecuteRequest { runtime: Python, ... }. This bypasses SDK interpreter-command registration entirely.left: "original\n",right: "EDITED\n"on its first post-execution host read. The bridge bundle was built from that checkout; no application storage service is needed.Suspected cause
The wire
WriteFilehandler mirrors bytes into the VM's root staging/shadow tree.service_owned_python_filesystem_rpc_requesthandles PythonWriteby updating only the kernel VFS. Subsequent shadow-to-kernel reconciliation imports the unchanged staging copy and overwrites the newer Python bytes. The JavaScript mirroring change in #1981 does not run on this Python RPC path.I am preparing a focused fix and regressions for Python writes, including read-modify-write and atomic replacement. Python append-position/truncate behavior and shell/WASM shadow writes are separate from this fix.