Solution: Use Pyodide v0.29.4 (Python 3.12)
The Chrome stack overflow issue has been resolved by upgrading to Pyodide v0.29.4. See PYODIDE_UPGRADE_NOTES.md for upgrade path.
The application is a Pyodide-based VPython runner built with SvelteKit. When using Pyodide v0.28.3 and deployed to Google Cloud Storage, Chrome users encountered a fatal error:
RangeError: Maximum call stack size exceeded
- Error only occurred in Chrome (not other browsers like Firefox/Safari)
- Error only occurred when DevTools was CLOSED
- Worked fine when DevTools was OPEN
- Error happened during Python module imports from packages in the filesystem
- Stack trace showed the error deep in pyodide.asm.wasm (Python's C internals)
Chrome V8 + Pyodide v0.28.3 WASM interaction bug:
The issue was in Python's import resolution phase within the WASM module when interacting with Chrome's V8 engine. The bug was present in Pyodide v0.28.3 but has been fixed in v0.29.0.
No amount of JavaScript delays, Python code modifications, or import restructuring could fix this because:
- The crash happened in WASM/C code (Python's import machinery)
- It occurred before any user Python code executed
- It was a timing-dependent race condition in Chrome's V8 engine
- The only temporary "fix" was Chrome running slower (DevTools open)
Implemented in v2.0.0:
- Updated
src/routes/+page.svelte:25to usehttps://cdn.jsdelivr.net/pyodide/v0.29.4/full/ - Removed unnecessary delay workarounds from
src/lib/utils/utils.js - Simplified import logic in
src/routes/+page.svelte(no more delays needed) - Tested and verified: NumPy and vpython imports work in Chrome with DevTools closed
- Main page:
src/routes/+page.svelte - Receives program code via
postMessagefrom trusted parent window - Loads Pyodide and executes Python code in browser
- Custom VPython library adapted for Pyodide/browser execution
- Packaged as
vpython.zipand deployed to Cloud Storage - Provides 3D graphics primitives via JavaScript interop
- SvelteKit builds static site to
build/ - Files uploaded to
gs://wmvprunner/bucket - Uses
@sveltejs/adapter-staticfor static site generation
File: do_build.sh
# Build vpython package first
npm run zip
# Clean old artifacts
gsutil -m rm -r gs://wmvprunner/_app/ 2>/dev/null || true
gsutil rm gs://wmvprunner/index.html gs://wmvprunner/favicon.png 2>/dev/null || true
# Set CORS on bucket
gsutil cors set cors.json gs://wmvprunner
# Build the app (vpython.zip lands in build/ via static/)
npm run build
# Upload with cache headers
gsutil -m -h "Cache-Control:public, max-age=3600" cp -r build/* gs://wmvprunner/
# Set proper content types and no-cache for index
gsutil setmeta -h "Cache-Control:no-cache, no-store, must-revalidate" gs://wmvprunner/index.html
gsutil -m setmeta -h "Content-Type:application/zip" -h "Cache-Control:no-cache" gs://wmvprunner/vpython.zippackage.json- npm scripts and dependenciessvelte.config.js- SvelteKit configuration with static adapterdo_build.sh- Build and deployment script
src/routes/+page.svelte:40-99- Main app logic, message handling, Pyodide setupsrc/lib/utils/utils.js:5-30- Pyodide initialization and package loadingsrc/routes/+page.svelte:169-197- Python code execution with async/await transformations
vpython/__init__.py- Package exports and initializationvpython/core_funcs.py- Wrapper functions for GlowScript JavaScript objectsvpython/vector.py- Vector math implementationvpython/vec_js.py- JavaScript-backed vector classvpython/shapespaths_orig.py- Path and shape definitions
vpython/__init__.py
├── core_funcs.py
│ ├── js imports (sphere, box, rate, etc.)
│ ├── vec_js.py
│ │ └── vector.py
│ └── shapes_piodide.py
├── shapespaths_orig.py
│ ├── vec_js.py (already imported)
│ └── vector.py (mag, norm functions)
├── vector.py (direct imports)
├── vec_js.py (already imported)
└── color.py
Note: While there are multiple imports of the same modules, Python's import system handles this correctly with module caching. The issue was not the imports themselves but the timing of cache operations.
PUBLIC_TRUSTED_HOST- Parent window origin for postMessage security- Set in
.envfile - Injected at build time
- Used to validate incoming messages
- Set in
src/routes/+page.svelte:43-56 validates:
- Origin matches
PUBLIC_TRUSTED_HOST - Message data exists and is a string
- Prevents execution of untrusted code
- Deploy using
./do_build.sh - Test in Chrome with DevTools closed
- Verify no stack overflow errors
- Test program execution works correctly
- Check browser network tab for proper cache headers on
vpython.zip
- The
sleep()function invpython/__init__.py:15-18uses a tight loop withrate(60)- this is intentional and not the cause of the issue - Pyodide version: v0.29.4 (loaded from CDN)
- GlowScript library loaded dynamically based on version in program code