sinelaw/fresh · error

editor.spawnHostProcess is not implemented (missing…

Error message

editor.spawnHostProcess is not implemented (missing _spawnHostProcessStart)

What it means

editor.spawnHostProcess is a JS shim that requires the Rust host binding editor._spawnHostProcessStart to exist; it forwards to a real ProcessHandle whose kill() calls _killHostProcess. When the binding is missing, the shim throws because host process spawning (and its kill support) cannot be implemented generically. The shim also deliberately only passes real strings so the Rust Opt<String> stays None for an omitted cwd instead of "", avoiding Command::current_dir("") ENOENT failures.

Solutions

  1. Ensure the runtime registers _spawnHostProcessStart (and _killHostProcess) — upgrade fresh-plugin-runtime or rebuild with host-spawn support enabled.
  2. Feature-detect editor._spawnHostProcessStart before calling spawnHostProcess and fall back to a supported API (e.g. editor.spawnProcess).
  3. Pass cwd as undefined rather than "" so the Rust Opt<String> stays None and avoids Command::current_dir("") ENOENT once the binding exists.
  4. If host process spawning is intentionally disabled on the target, restructure the plugin to request capabilities from the host instead.

Example fix

// before
const p = editor.spawnHostProcess("make", ["build"], "");

// after
if (typeof editor._spawnHostProcessStart !== "function") {
  throw new Error("host process spawning unavailable");
}
const p = editor.spawnHostProcess("make", ["build"], undefined);
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof editor._spawnHostProcessStart !== "function") {
  throw new Error("host process spawning not supported in this runtime");
}

Type guard

function supportsHostSpawn(editor) {
  return typeof editor._spawnHostProcessStart === "function" &&
         typeof editor.spawnHostProcess === "function";
}

Try / catch

try {
  handle = editor.spawnHostProcess(cmd, args, cwd);
} catch (e) {
  if (String(e.message).includes("_spawnHostProcessStart")) {
    // fall back to editor.spawnProcess or non-process approach
  } else throw e;
}

Prevention

When it happens

Trigger: Calling editor.spawnHostProcess(cmd, args, cwd) when editor._spawnHostProcessStart is not a function — the quickjs_backend.rs runtime never installed the host-process binding (build without host-spawn support, older runtime, or a sandbox that excludes host process APIs).

Common situations: Plugins spawning host processes on a backend that lacks the _killHostProcess/_spawnHostProcessStart pair; runtime version drift between plugin API expectations and installed editor; restricted sandboxes where host process access is intentionally disabled.

Related errors


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

Appendix: source

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

                            }
                            return false;
                        },
                        then(onFulfilled, onRejected) {
                            return resultPromise.then(onFulfilled, onRejected);
                        },
                        catch(onRejected) {
                            return resultPromise.catch(onRejected);
                        }
                    };
                };
                // spawnHostProcess gets a bespoke wrapper (instead of
                // `_wrapAsyncThenable`) because its `ProcessHandle`
                // exposes a real `kill()` that forwards to
                // `_killHostProcess`. Generic wrap has no hook for
                // that.
                editor.spawnHostProcess = function(command, args, cwd) {
                    if (typeof editor._spawnHostProcessStart !== 'function') {
                        throw new Error('editor.spawnHostProcess is not implemented (missing _spawnHostProcessStart)');
                    }
                    // Pass real strings only. Earlier revisions forwarded
                    // `""` for a missing cwd, which landed verbatim as
                    // `Command::current_dir("")` in the dispatcher —
                    // every host-spawn then failed with ENOENT. Use two
                    // arity forms so the Rust `Opt<String>` stays `None`
                    // instead of `Some("")`.
                    let callbackId;
                    if (typeof cwd === "string" && cwd.length > 0) {
                        callbackId = editor._spawnHostProcessStart(command, args || [], cwd);
                    } else {
                        callbackId = editor._spawnHostProcessStart(command, args || []);
                    }
                    const resultPromise = new Promise(function(resolve, reject) {
                        globalThis._pendingCallbacks.set(callbackId, { resolve: resolve, reject: reject });
                    });
                    return {
                        processId: callbackId,

View on GitHub (pinned to 67894ca546)