sinelaw/fresh · error

Failed to create QuickJS context for plugin

Error message

Failed to create QuickJS context for plugin {}: {}

What it means

When loading a plugin that has no dedicated JS context yet, the backend creates one via Context::full(&self.runtime). Failure aborts the plugin load with this error naming the plugin and wrapping the rquickjs error.

Solutions

  1. Check memory limits and reduce the number of concurrently loaded plugins
  2. Retry the plugin load after freeing resources
  3. Inspect the wrapped rquickjs error for the concrete cause
  4. Restart the plugin host/backend if runtime state is corrupted

Example fix

// before
// load all 50 plugins at startup under 64MB limit
// after
// raise limit and load lazily
docker run -m 512m my-host
for plugin in needed_plugins { backend.load_module(plugin).await?; }
Defensive patterns

Strategy: try-catch

Validate before calling

// bound concurrent plugin loads to avoid context-creation pressure
if active_plugins() >= MAX_PLUGINS { return Err("plugin limit reached"); }

Try / catch

match backend.load_module(path).await {
    Err(e) if e.to_string().contains("Failed to create QuickJS context for plugin") => {
        eprintln!("could not init context for plugin: {e:#}");
        unload_unused_plugins();
        retry_once()
    }
    other => other,
}

Prevention

When it happens

Trigger: Context allocation fails while lazily initializing a per-plugin context — runtime memory exhaustion, corrupted runtime state, or an rquickjs internal error during plugin load.

Common situations: Loading many plugins simultaneously in a memory-limited host; loading a plugin after the runtime entered a bad state (e.g. after OOM); rquickjs native library issues.

Related errors


AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13). Data as JSON: /api/errors/113d5136b63ee6af. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-plugin-runtime/src/backend/quickjs_backend.rs:9117

        let plugin_name = Path::new(source_name)
            .file_stem()
            .and_then(|s| s.to_str())
            .unwrap_or("unknown");

        tracing::debug!(
            "execute_js: starting for plugin '{}' from '{}'",
            plugin_name,
            source_name
        );

        // Get or create context for this plugin
        let context = {
            let mut contexts = self.plugin_contexts.borrow_mut();
            if let Some(ctx) = contexts.get(plugin_name) {
                ctx.clone()
            } else {
                let ctx = Context::full(&self.runtime).map_err(|e| {
                    anyhow!(
                        "Failed to create QuickJS context for plugin {}: {}",
                        plugin_name,
                        e
                    )
                })?;
                self.setup_context_api(&ctx, plugin_name)?;
                contexts.insert(plugin_name.to_string(), ctx.clone());
                ctx
            }
        };

        // Wrap plugin code in IIFE to prevent TDZ errors and scope pollution
        // This is critical for plugins like vi_mode that declare `const editor = ...`
        // which shadows the global `editor` causing TDZ if not wrapped.
        let wrapped_code = format!("(function() {{ {} }})();", code);
        let wrapped = wrapped_code.as_str();

        context.with(|ctx| {

View on GitHub (pinned to 67894ca546)