ethereum/go-ethereum · error
js error: timer/timeout callback is not a function
Error message
js error: timer/timeout callback is not a function
What it means
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.
Source
Thrown at internal/jsre/jsre.go:202
loop:
for {
select {
case timer := <-ready:
// execute callback, remove/reschedule the timer
var arguments []interface{}
if len(timer.call.Arguments) > 2 {
tmp := timer.call.Arguments[2:]
arguments = make([]interface{}, 2+len(tmp))
for i, value := range tmp {
arguments[i+2] = value
}
} else {
arguments = make([]interface{}, 1)
}
arguments[0] = timer.call.Arguments[0]
call, isFunc := goja.AssertFunction(timer.call.Arguments[0])
if !isFunc {
panic(re.vm.ToValue("js error: timer/timeout callback is not a function"))
}
call(goja.Null(), timer.call.Arguments[2:]...)
_, inreg := registry[timer] // when clearInterval is called from within the callback don't reset it
if timer.interval && inreg {
timer.timer.Reset(timer.duration)
} else {
delete(registry, timer)
if waitForCallbacks && (len(registry) == 0) {
break loop
}
}
case req := <-re.evalQueue:
// run the code, send the result back
req.fn(re.vm)
close(req.done)
if waitForCallbacks && (len(registry) == 0) {
break loopView on GitHub (pinned to 6bb0588ad8)
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.
Example fix
// before
setTimeout("checkPeers()", 1000)
// after
setTimeout(function(){ checkPeers(); }, 1000) Defensive patterns
Strategy: type-guard
Validate before calling
// inside console scripts / JS before scheduling
function safeSetInterval(cb, ms) {
if (typeof cb !== 'function') {
throw new Error('callback must be a function');
}
return setInterval(cb, ms);
} Type guard
function isFunction(v) { return typeof v === 'function'; } Try / catch
Wrap timer setup in try/catch in JS scripts; on error, log the offending callback value and skip scheduling.
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Porting browser JS snippets into geth console scripts; copy-pasted tutorials using string-eval style timers; typos where the function reference is misspelled (undefined).
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
AI-assisted analysis of ethereum/go-ethereum@6bb0588ad8 (2026-08-15).
Data as JSON: /api/errors/fb6c22b84e467682.
Report an issue: GitHub.