{"record":{"id":"fb6c22b84e467682","repo":"ethereum/go-ethereum","slug":"js-error-timer-timeout-callback-is-not-a-function","errorCode":null,"errorMessage":"js error: timer/timeout callback is not a function","messagePattern":"js error: timer/timeout callback is not a function","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/jsre/jsre.go","lineNumber":202,"sourceCode":"loop:\n\tfor {\n\t\tselect {\n\t\tcase timer := <-ready:\n\t\t\t// execute callback, remove/reschedule the timer\n\t\t\tvar arguments []interface{}\n\t\t\tif len(timer.call.Arguments) > 2 {\n\t\t\t\ttmp := timer.call.Arguments[2:]\n\t\t\t\targuments = make([]interface{}, 2+len(tmp))\n\t\t\t\tfor i, value := range tmp {\n\t\t\t\t\targuments[i+2] = value\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\targuments = make([]interface{}, 1)\n\t\t\t}\n\t\t\targuments[0] = timer.call.Arguments[0]\n\t\t\tcall, isFunc := goja.AssertFunction(timer.call.Arguments[0])\n\t\t\tif !isFunc {\n\t\t\t\tpanic(re.vm.ToValue(\"js error: timer/timeout callback is not a function\"))\n\t\t\t}\n\t\t\tcall(goja.Null(), timer.call.Arguments[2:]...)\n\n\t\t\t_, inreg := registry[timer] // when clearInterval is called from within the callback don't reset it\n\t\t\tif timer.interval && inreg {\n\t\t\t\ttimer.timer.Reset(timer.duration)\n\t\t\t} else {\n\t\t\t\tdelete(registry, timer)\n\t\t\t\tif waitForCallbacks && (len(registry) == 0) {\n\t\t\t\t\tbreak loop\n\t\t\t\t}\n\t\t\t}\n\t\tcase req := <-re.evalQueue:\n\t\t\t// run the code, send the result back\n\t\t\treq.fn(re.vm)\n\t\t\tclose(req.done)\n\t\t\tif waitForCallbacks && (len(registry) == 0) {\n\t\t\t\tbreak loop","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/ethereum/go-ethereum/blob/6bb0588ad8e7f922e4ad5580f51265a4097af08f/internal/jsre/jsre.go#L184-L220","documentation":"The JavaScript runtime (goja) in geth's console validates that the first argument of setTimeout/setInterval is a callable function before invoking it. Unlike browsers, geth's JS environment does not accept strings as timer callbacks; passing anything non-callable makes the event loop panic with this message, which surfaces as a JS error in the console.","triggerScenarios":"In the geth attach/console: setTimeout(\"doSomething()\", 1000), setTimeout(42, 1000), or setInterval(null, 500) — any call whose first argument is not a function value.","commonSituations":"Porting browser JS snippets into geth console scripts; copy-pasted tutorials using string-eval style timers; typos where the function reference is misspelled (undefined).","solutions":["Pass an actual function: setTimeout(function(){ doSomething(); }, 1000) or setTimeout(doSomething, 1000).","Verify the callback is defined before scheduling: if (typeof cb === 'function') { setInterval(cb, 500) }.","Avoid string-based eval patterns; they are unsupported in goja timers."],"exampleFix":"// before\nsetTimeout(\"checkPeers()\", 1000)\n\n// after\nsetTimeout(function(){ checkPeers(); }, 1000)","handlingStrategy":"type-guard","validationCode":"// inside console scripts / JS before scheduling\nfunction safeSetInterval(cb, ms) {\n    if (typeof cb !== 'function') {\n        throw new Error('callback must be a function');\n    }\n    return setInterval(cb, ms);\n}","typeGuard":"function isFunction(v) { return typeof v === 'function'; }","tryCatchPattern":"Wrap timer setup in try/catch in JS scripts; on error, log the offending callback value and skip scheduling.","preventionTips":["Never pass strings to setTimeout/setInterval in geth console — goja does not eval them.","Check typeof cb === 'function' before scheduling dynamic callbacks.","Define all functions before the timer registration lines in scripts."],"tags":["jsre","console","goja","settimeout","javascript"],"backgroundTag":null,"analyzedSha":"6bb0588ad8e7f922e4ad5580f51265a4097af08f","analyzedAt":"2026-08-15T10:06:53.996Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}