{"record":{"id":"3bd1e2006c417524","repo":"GopeedLab/gopeed","slug":"execute-expects-a-string-or-function","errorCode":null,"errorMessage":"execute expects a string or function","messagePattern":"execute expects a string or function","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/download/engine/webview/runtime.go","lineNumber":574,"sourceCode":"\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}\n\t\t\treturn normalizeFunctionSource(source), nil\n\t\t}\n\t}\n\tsource := strings.TrimSpace(value.String())\n\tif looksLikeFunctionSource(source) {\n\t\treturn normalizeFunctionSource(source), nil","sourceCodeStart":556,"sourceCodeEnd":592,"githubUrl":"https://github.com/GopeedLab/gopeed/blob/7b7327ffb30816273a74b142cccc0bc10c5a4c67/pkg/download/engine/webview/runtime.go#L556-L592","documentation":"NormalizeExecutableValue is the goja.Value-level entry for executable arguments; its first guard rejects a nil value. A nil reaches it when Go code passes a literal nil (or a nil goja.Value interface) — from the JS side, undefined/null normally take the normalizeExecutable default branch instead, so this is essentially a Go-caller bug.","triggerScenarios":"Calling NormalizeExecutableValue(nil) or a wrapper that forwards an uninitialized goja.Value; Execute helpers reading a map key that was never set and forwarding the zero Value.","commonSituations":"Host-side plumbing that extracts a callback from an options object and forwards it without checking presence; refactors that dropped an existence check.","solutions":["Check the value exists (Get returns non-undefined) before forwarding to NormalizeExecutableValue","Default to a sensible expression string when the argument is absent","Unit-test the host helper with the missing-argument case"],"exampleFix":"// before\nval := opts.Get(\"fn\") // may be nil\nsrc, err := webview.NormalizeExecutableValue(val)\n// after\nval := opts.Get(\"fn\")\nif val == nil || goja.IsUndefined(val) || goja.IsNull(val) {\n    return fmt.Errorf(\"option 'fn' is required\")\n}\nsrc, err := webview.NormalizeExecutableValue(val)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// Go: presence check before normalizing a goja.Value\nfunc executablePresent(v goja.Value) bool {\n    return v != nil && !goja.IsUndefined(v) && !goja.IsNull(v)\n}","tryCatchPattern":null,"preventionTips":["Check options keys with Get + IsUndefined before forwarding values","Return a clear 'option X is required' error instead of forwarding nil","Cover the missing-argument path in host-helper tests"],"tags":["webview","execute","nil-guard","goja"],"backgroundTag":null,"analyzedSha":"7b7327ffb30816273a74b142cccc0bc10c5a4c67","analyzedAt":"2026-08-16T02:51:03.250Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}