{"record":{"id":"5b876b84d3d84ce1","repo":"GopeedLab/gopeed","slug":"execute-expects-a-string-or-function-got-t","errorCode":null,"errorMessage":"execute expects a string or function, got %T","messagePattern":"execute expects a string or function, got %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/download/engine/webview/runtime.go","lineNumber":568,"sourceCode":"\t}\n\treturn value\n}\n\nfunc defaultPollIntervalMS(value int64) int64 {\n\tif value <= 0 {\n\t\treturn 100\n\t}\n\treturn value\n}\n\nfunc normalizeExecutable(scriptOrFn any) (string, error) {\n\tswitch value := scriptOrFn.(type) {\n\tcase string:\n\t\treturn value, nil\n\tcase *goja.Object:\n\t\treturn NormalizeExecutableValue(value)\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"execute expects a string or function, got %T\", scriptOrFn)\n\t}\n}\n\nfunc NormalizeExecutableValue(value goja.Value) (string, error) {\n\tif value == nil {\n\t\treturn \"\", fmt.Errorf(\"execute expects a string or function\")\n\t}\n\tswitch raw := value.Export().(type) {\n\tcase string:\n\t\treturn raw, nil\n\t}\n\tobj, ok := value.(*goja.Object)\n\tif ok {\n\t\tif _, ok := goja.AssertFunction(obj); ok {\n\t\t\tsource, err := functionSource(obj)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}","sourceCodeStart":550,"sourceCodeEnd":586,"githubUrl":"https://github.com/GopeedLab/gopeed/blob/7b7327ffb30816273a74b142cccc0bc10c5a4c67/pkg/download/engine/webview/runtime.go#L550-L586","documentation":"normalizeExecutable type-switches the argument of Execute/WaitForFunction: only a Go string (a JS expression) or a *goja.Object (a JS function value) are accepted. Anything else — numbers, booleans, slices, maps, nil, goja.Undefined — hits this branch and the concrete Go type is reported via %T.","triggerScenarios":"page.execute(42), page.execute(null), or page.execute(['#sel']) from JS (undefined/null export to non-*goja.Object Go values); Go callers passing an int, []string, or map[string]any as scriptOrFn.","commonSituations":"Passing a selector string wrapped in a variable that is actually an array of selectors; passing undefined because an optional argument was never supplied; passing a parsed JSON value instead of source text.","solutions":["Pass a string expression ('document.title') or an actual function (() => document.title)","If the value is dynamic, coerce with String(x) first when it is genuinely a string","For selectors use dedicated APIs (click/waitForSelector) instead of execute"],"exampleFix":"// before\nconst sel = getSelector(); // number 42\nawait page.execute(sel);\n// after\nconst sel = String(getSelector());\nawait page.execute(`() => document.querySelector(${JSON.stringify(sel)})`);","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// JS: accept only string or function before execute\nfunction isExecutable(x) {\n  return typeof x === 'string' || typeof x === 'function';\n}\nif (!isExecutable(target)) throw new TypeError(`execute needs string|function, got ${typeof target}`);","tryCatchPattern":null,"preventionTips":["Pass source strings or arrow functions only — never selectors/arrays/numbers","Use click/waitForSelector APIs for selector-based work","Coerce only when the value is genuinely a string of source code"],"tags":["webview","execute","type-error","javascript"],"backgroundTag":null,"analyzedSha":"7b7327ffb30816273a74b142cccc0bc10c5a4c67","analyzedAt":"2026-08-16T02:51:03.250Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}