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 loop

View on GitHub (pinned to 6bb0588ad8)

Solutions

  1. Pass an actual function: setTimeout(function(){ doSomething(); }, 1000) or setTimeout(doSomething, 1000).
  2. Verify the callback is defined before scheduling: if (typeof cb === 'function') { setInterval(cb, 500) }.
  3. 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

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


AI-assisted analysis of ethereum/go-ethereum@6bb0588ad8 (2026-08-15). Data as JSON: /api/errors/fb6c22b84e467682. Report an issue: GitHub.