Troubleshooting¶
This guide is keyed by what you see: an error inside the guest, a field of the result, or an exception from run. Find the symptom, then apply the fix.
Remember the default first. A call with no arguments can read the stdlib and compute, but it has no /, nothing writable, no network and no threads. Most surprises follow from that.
OSError or PermissionError when the program writes¶
What you see: open('x', 'w'), tempfile, np.save or df.to_csv('x.csv') fails with OSError. Or a write into a granted folder fails with PermissionError.
Why: by default nothing is writable. A grant is read-only unless it says writable=True, and a read-only grant refuses every write with EPERM.
Fix: give the call somewhere to write.
root=Truegives a fresh, private, writable/, removed after the call.Grant(folder, "/out", writable=True)gives one writable host folder.
from pathlib import Path
from wasi_python import Grant, run
data = Path.home() / "project" / "data"
done = run(
["-c", "import os; open('/out/n.txt', 'w').write(str(len(os.listdir('/data'))))"],
grants=[Grant(data, "/data"), Grant("/tmp/job-17", "/out", writable=True)],
)
See Grants and the guest filesystem.
A relative path is not found¶
What you see: FileNotFoundError for a path such as data.csv.
Why: with no root, the guest has no / and so no working folder.
Fix: use the absolute guest path of a grant (/data/data.csv), or give the call a root.
A library cannot find a temporary or cache folder¶
What you see: import matplotlib raises OSError ("Matplotlib requires access to a writable cache directory"). openpyxl's save raises FileNotFoundError ("No usable temporary directory"). df.to_excel(…) raises xlsxwriter.exceptions.FileCreateError.
Why: these libraries write through /tmp or HOME, and neither is writable by default.
Fix: give the call a writable folder at /tmp (Grant(folder, "/tmp", writable=True), or root=True). For matplotlib, you can instead set MPLCONFIGDIR in env= to a folder inside a writable grant. To write .xlsx with nothing writable, use xlsxwriter in memory. Inside the guest program, with buf = io.BytesIO():
pd.ExcelWriter(buf, engine="xlsxwriter", engine_kwargs={"options": {"in_memory": True}})
xlsxwriter.Workbook(buf, {"in_memory": True})
See matplotlib.
trapped is "out of fuel"¶
What you see: done.trapped == "out of fuel" and done.exit_code is None.
Why: the program used the fuel cap you gave. Fuel counts the guest's executed instructions. Imports count too, and so does the interpreter's finalization after the program's last line. A cap that runs out there still stops the call, after the program's output was written.
Fix: raise fuel, with room for finalization. Read done.fuel_used from a run that passed to size the cap. See Fuel.
trapped is "deadline"¶
What you see: done.trapped == "deadline".
Why: the call ran past deadline_s, 60 seconds by default. Each call pays for its own imports, since the snapshot pre-imports no package; import pandas is a large part of a small job.
Fix: raise deadline_s, or pass None for no deadline. To pay an import once for several steps, put the steps in one call, or use a session, whose later turns keep what an earlier turn imported.
Output is missing after a stop¶
What you see: a trapped call's stdout lacks lines the program printed.
Why: output is buffered as CPython buffers it without -u. A program stopped by fuel, a deadline or os._exit loses what it printed since its last flush.
Fix: add -u to the program's arguments, as you would for python, or flush before the end.
truncated is not empty¶
What you see: done.truncated == ("stdout",), and stdout ends early.
Why: a stream passed output_bytes, 16 MiB by default. The bytes past it were dropped.
Fix: raise output_bytes, or write large results to a writable grant and read the file on the host.
EMFILE: too many open files¶
What you see: an open inside the guest fails with EMFILE.
Why: the guest holds max_open_files files and folders open at once, 1,024 by default, across grants, the root and mounts. The guest runs on after the refusal.
Fix: close files in the program, or raise max_open_files. Also check your own process's soft descriptor limit: a guest's descriptors share your process's table, and macOS starts a process at 256. Raise the soft limit at start:
import resource # Linux and macOS; Windows has no resource module
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
resource.setrlimit(resource.RLIMIT_NOFILE, (hard, hard))
See Defaults and limits.
trapped is "out of file calls"¶
What you see: a call with mounts ends with trapped == "out of file calls".
Why: it made more provider calls than max_file_calls, 10,000 by default. Every provider call counts, whatever it answers.
Fix: raise max_file_calls, or pass None. See Mounts.
ModuleNotFoundError or ImportError¶
What you see: import some_package fails.
Why: the package is not in the artifact set. The guest cannot install packages and loads no shared library.
Fix: check the packages page. A pure-Python package can come from a read-only grant named in import_path=. A compiled one cannot be added. Some modules are absent with the platform, such as resource. pytz is not carried: use zoneinfo. See the limitations page.
RecursionError, or trapped is "stack overflow"¶
What you see: RecursionError after 40 to 250 levels, where native Python allows 1,000. Or the call ends with trapped == "stack overflow".
Why: the guest has a fixed stack. Recursion that runs through Python code raises RecursionError early. Some deep recursion in C, such as freeing a very deeply nested list, still traps. A trap ends only that call.
Fix: make the recursion shallower, or iterate instead. See the limitations page.
RuntimeError: can't start new thread¶
What you see: starting a thread, or a library that starts one, raises RuntimeError.
Why: the guest has no threads.
Fix: use the serial form. Many libraries already run serially here: joblib's Parallel and scikit-learn's n_jobs give n_jobs=1's result. For scipy, keep workers at its default, 1.
Error numbers differ on Windows or from Linux¶
What you see: [Errno 44] for a missing file, where Linux says [Errno 2]. Or a program that works on Linux and macOS fails on a Windows host.
Why: errno values inside the guest are WASI's, not Linux's. And a writable grant answers as the host's file system does: on a Windows grant, a file the guest holds open cannot be removed or renamed, so tempfile.TemporaryFile fails there.
Fix: compare with errno names or catch FileNotFoundError, never a number. For the Windows differences, see On Windows on the limitations page and on Mounts; a mount answers the same on every host.
TypeError or ValueError before anything runs¶
What you see: run raises at once.
Why: an argument has the wrong type or is out of range. A limit of True, a negative cap or a deadline of 0 is refused, so no value can quietly remove a limit. A grant's guest path must be absolute and not / or under /usr/local/lib. On Windows, a host path with a reserved name (CON, NUL, COM1 and the like) is refused.
Fix: correct the argument. See the Python API.
RuntimeError before the guest runs¶
What you see: run raises RuntimeError and no guest code ran.
Why, most often:
- the process was forked after it ran a guest; wasmtime does not support use across a fork;
- on Linux,
faulthandler,signal.signalor a crash reporter was installed or removed after the first call; - the host is CPython 3.14.0 on musl, or a system whose thread stack the runner cannot read.
Fix: start guests only after a fork (in gunicorn's post_fork, for example). Install faulthandler and crash reporters before the first call. Use CPython 3.14.1 or later on musl.
ArtifactsMissing¶
What you see: run raises ArtifactsMissing.
Why: the runner found no artifact set: not at $WASI_PYTHON_ARTIFACTS, not in a source checkout's build/artifacts/, and not in the wheel.
Fix: install the released wheel (pip install wasi-python), or point WASI_PYTHON_ARTIFACTS at a folder holding the set and its MANIFEST. See Artifacts.
SessionEnded¶
What you see: Session.run raises SessionEnded.
Why: the session has ended. session.ended says why: "idle", "lifetime", a turn that called exit(), or a turn that was stopped (its trapped).
Fix: open a new session. See Sessions.
In one line¶
Most errors come from the default sandbox (nothing writable, no threads, no network) or a limit; grant a folder, raise the limit, or check the packages and limitations pages.