GopeedLab/gopeed · error

execute expects a string or function, got %T (%q)

Error message

execute expects a string or function, got %T (%q)

What it means

The typed variant of the execute argument error, produced by the fallback branch of NormalizeExecutableValue (pkg/download/engine/webview/runtime.go:590-594). The value was present but could not be interpreted: it is not a string, not a callable function (detected via goja.AssertFunction and serialized with toString), and its string form does not look like function source. The message includes the Go type (%T) and the coerced value (%q) to identify exactly what was passed.

Source

Thrown at pkg/download/extension_runtime_webview.go:146

		return goja.Undefined()
	})
	_ = obj.Set("goto", func(call goja.FunctionCall) goja.Value {
		url, err := requireStringArg(call, 0, "url")
		if err != nil {
			panic(vm.ToValue(err))
		}
		if err := page.Goto(
			url,
			optionalMap(call.Argument(1)),
		); err != nil {
			panic(vm.ToValue(err))
		}
		return goja.Undefined()
	})
	_ = obj.Set("execute", func(call goja.FunctionCall) goja.Value {
		expression, err := enginewebview.NormalizeExecutableValue(call.Argument(0))
		if err != nil {
			panic(vm.ToValue(err))
		}
		result, err := page.Execute(expression, exportArgs(call.Arguments[1:])...)
		if err != nil {
			panic(vm.ToValue(err))
		}
		return vm.ToValue(result)
	})
	_ = obj.Set("focus", func(call goja.FunctionCall) goja.Value {
		selector, err := requireStringArg(call, 0, "selector")
		if err != nil {
			panic(vm.ToValue(err))
		}
		if err := page.Focus(selector); err != nil {
			panic(vm.ToValue(err))
		}
		return goja.Undefined()
	})
	_ = obj.Set("click", func(call goja.FunctionCall) goja.Value {

View on GitHub (pinned to 7b7327ffb3)

Solutions

  1. Pass a string containing a JS expression: page.execute("document.querySelector('#dl').href")
  2. Or pass an actual function: page.execute(() => document.title)
  3. If the value is dynamic, stringify first and verify it is non-empty: page.execute(String(expr))

Example fix

// before
const expr = config.expr; // number 42 from settings
page.execute(expr);

// after
if (typeof config.expr !== "string" && typeof config.expr !== "function") {
  throw new TypeError("expr must be a string or function");
}
page.execute(config.expr);
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof expr !== "string" && typeof expr !== "function") {
  throw new TypeError(`execute expects string or function, got ${typeof expr}`);
}

Type guard

function isExecutable(v) {
  return typeof v === "string" || typeof v === "function";
}

Try / catch

try {
  page.execute(expr);
} catch (e) {
  if (String(e.message ?? e).includes("execute expects")) {
    gopeed.logger.warn(`invalid execute argument: ${typeof expr}`);
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Passing numbers (page.execute(42)), booleans, plain objects or arrays as the first argument; passing undefined or null explicitly (they export to non-callable, non-string values); passing a Proxy or class instance that is not itself callable; passing a string like "42" is fine, but "4 2" style non-source strings are not.

Common situations: Forwarding a parsed JSON value (object) instead of the desired expression string; passing a DOM element wrapper or handle returned by another API; passing a function-like string that got truncated; undefined leaking from optional chaining.

Related errors


AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16). Data as JSON: /api/errors/61916c29e34bedcd. Report an issue: GitHub.