sinelaw/fresh · error

JS error in : : Stack trace

Error message

JS error in {}: {}: {}
Stack trace:
{}

What it means

format_js_error converts a caught rquickjs exception into a Rust anyhow error. When the exception object exposes a non-empty 'stack' property, the error includes the source name, the JS error name and message, and the full JS stack trace.

Solutions

  1. Read the stack trace in the error to locate the throwing JS line in your plugin
  2. Fix the offending JS (undefined function/property, bad arguments, etc.)
  3. Wrap risky plugin code in try/catch and reject promises explicitly
  4. Re-run with the updated plugin and confirm the error is gone

Example fix

// before (plugin.js)
const cfg = config.settings.timeout; // settings is undefined
// after
const cfg = config.settings?.timeout ?? 3000;
Defensive patterns

Strategy: try-catch

Validate before calling

// before executing a plugin, lint it for obvious ReferenceErrors/TypeErrors
npx eslint --no-eslintrc --env browser plugin.js

Try / catch

match backend.execute_js(src) {
    Ok(v) => v,
    Err(e) if e.to_string().contains("JS error in") => {
        tracing::error!("plugin failed: {e:#}");
        // disable plugin or fall back
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Plugin JavaScript code throws or has an uncaught exception (TypeError, ReferenceError, user-thrown Error) during execute_js; the exception is captured via the promise rejection tracker or eval and formatted with its stack.

Common situations: Calling an undefined function or property in a plugin; bad arguments to the plugin API; an async plugin callback rejecting; a typo introduced in plugin code after an update.

Related errors


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

Appendix: source

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

    source_name: &str,
) -> anyhow::Error {
    // Check if this is an exception that we can catch for more details
    if err.is_exception() {
        // Try to catch the exception to get the full error object
        let exc = ctx.catch();
        if !exc.is_undefined() && !exc.is_null() {
            // Try to get error message and stack from the exception object
            if let Some(exc_obj) = exc.as_object() {
                let message: String = exc_obj
                    .get::<_, String>("message")
                    .unwrap_or_else(|_| "Unknown error".to_string());
                let stack: String = exc_obj.get::<_, String>("stack").unwrap_or_default();
                let name: String = exc_obj
                    .get::<_, String>("name")
                    .unwrap_or_else(|_| "Error".to_string());

                if !stack.is_empty() {
                    return anyhow::anyhow!(
                        "JS error in {}: {}: {}\nStack trace:\n{}",
                        source_name,
                        name,
                        message,
                        stack
                    );
                } else {
                    return anyhow::anyhow!("JS error in {}: {}: {}", source_name, name, message);
                }
            } else {
                // Exception is not an object, try to convert to string
                let exc_str: String = exc
                    .as_string()
                    .and_then(|s: &rquickjs::String| s.to_string().ok())
                    .unwrap_or_else(|| format!("{:?}", exc));
                return anyhow::anyhow!("JS error in {}: {}", source_name, exc_str);
            }
        }

View on GitHub (pinned to 67894ca546)