{"record":{"id":"414df0460e3a4c0c","repo":"cayleygraph/cayley","slug":"expected-js-callback-function","errorCode":null,"errorMessage":"expected js callback function","messagePattern":"expected js callback function","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"query/gizmo/gizmo.go","lineNumber":193,"sourceCode":"\tctx := s.context()\n\n\toutput := make([]interface{}, 0)\n\terr := iterator.Iterate(ctx, it).Paths(false).Limit(limit).EachValue(s.qs, func(v quad.Value) error {\n\t\tif o := s.quadValueToNative(v); o != nil {\n\t\t\toutput = append(output, o)\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn output, nil\n}\n\nfunc (s *Session) runIteratorWithCallback(it iterator.Shape, callback goja.Value, this goja.FunctionCall, limit int) error {\n\tfnc, ok := goja.AssertFunction(callback)\n\tif !ok {\n\t\treturn fmt.Errorf(\"expected js callback function\")\n\t}\n\tctx, cancel := context.WithCancel(s.context())\n\tdefer cancel()\n\treturn iterator.Iterate(ctx, it).Paths(true).Limit(limit).TagEach(func(tags map[string]graph.Ref) error {\n\t\ttm, err := s.tagsToValueMap(tags)\n\t\tif err != nil || tm == nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = fnc(this.This, s.vm.ToValue(tm))\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t}\n\t\treturn err\n\t})\n}\n\nfunc (s *Session) send(ctx context.Context, r *Result) bool {\n\tif s.limit > 0 && s.count >= s.limit {","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/cayleygraph/cayley/blob/81dcd7d73e45136bc0d01802a8ba4685d8a533eb/query/gizmo/gizmo.go#L175-L211","documentation":"runIteratorWithCallback expects the second argument of ForEach to be a callable JS function; goja.AssertFunction fails for anything else and the session returns this error. It is a plain Go error (returned, not panicked), so it surfaces as the result of ForEach/limit-bounded iteration.","triggerScenarios":"Calling s.ForEach(limit) or ForEach with a non-function second argument — e.g. a string, object, undefined, or null — at query/gizmo/gizmo.go:193.","commonSituations":"JS scripts that forget the callback (forEach(10) intending a limit), or pass an arrow function stored as an object property that is undefined at call time.","solutions":["Pass an actual function as the callback: .forEach(function(v){...}) or (v) => {...}.","If you intended a limit, use the correct overload/order: ForEach(limit, callback).","In Go embedding, ensure the goja.Value you pass is created from a JS function via vm.ToValue(func(...))."],"exampleFix":"// before\np.forEach(10)\n// after\np.forEach(function(v) { console.log(v); }, 10)","handlingStrategy":"type-guard","validationCode":"if (typeof callback !== 'function') throw new Error('forEach requires a callback function');","typeGuard":"function isFunction(v) { return typeof v === 'function'; }","tryCatchPattern":"try { p.forEach(cb, 10); } catch (e) { if (String(e).includes('expected js callback function')) { /* pass a function */ } throw e; }","preventionTips":["Always pass a function argument to forEach","Check argument order (limit vs callback) against the API docs","In Go embedding, wrap functions with vm.ToValue(fn) before passing"],"tags":["gizmo","javascript","invalid-argument"],"backgroundTag":"invalid-argument-value","analyzedSha":"81dcd7d73e45136bc0d01802a8ba4685d8a533eb","analyzedAt":"2026-09-06T06:14:12.358Z","contentChangedAt":"2026-09-06T06:14:12.358Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}