grafana/k6 · error
setTimeout requires a function as first argument
Error message
setTimeout requires a function as first argument
What it means
k6/experimental's setTimeout only supports a callable as its first argument; sobek binds the parameter as sobek.Callable, and any non-function value (including the browser-style string-eval form) binds as nil, which this guard rejects. Argument passing and clearTimeout are not supported yet (marked TODO in the source).
Source
Thrown at internal/js/modules/k6/experimental/experimental.go:48
}
// New returns a new RootModule.
func New() *RootModule {
return &RootModule{}
}
// Exports returns the exports of the experimental module
func (mi *ModuleInstance) Exports() modules.Exports {
return modules.Exports{
Named: map[string]any{
"setTimeout": mi.setTimeout,
},
}
}
func (mi *ModuleInstance) setTimeout(f sobek.Callable, t float64) {
if f == nil {
common.Throw(mi.vu.Runtime(), errors.New("setTimeout requires a function as first argument"))
}
// TODO maybe really return something to use with `clearTimeout
// TODO support arguments ... maybe
runOnLoop := mi.vu.RegisterCallback()
go func() {
timer := time.NewTimer(time.Duration(t * float64(time.Millisecond)))
select {
case <-timer.C:
runOnLoop(func() error {
_, err := f(sobek.Undefined())
return err
})
case <-mi.vu.Context().Done():
// TODO log something?
timer.Stop()
runOnLoop(func() error { return nil })
}View on GitHub (pinned to 93accf6570)
Solutions
- Pass a function reference: setTimeout(fn, 100)
- For arguments, wrap in a closure: setTimeout(() => fn(arg1, arg2), 100)
- If the delay value comes from config, ensure the function slot is a real function, not a serialized string
Example fix
// before
setTimeout(doWork('x'), 100);
// after
setTimeout(() => doWork('x'), 100); Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof callback !== 'function') throw new TypeError('setTimeout callback must be a function');
setTimeout(callback, delayMs); Type guard
/** @param {unknown} v @returns {v is Function} */
const isCallable = (v) => typeof v === 'function';
if (!isCallable(maybeFn)) throw new TypeError(`expected function, got ${typeof maybeFn}`); Prevention
- Never port browser string-argument setTimeout code verbatim
- Pass the reference, not the call result: setTimeout(fn) not setTimeout(fn())
- Wrap arguments in a closure because k6's setTimeout does not forward them (source TODO)
When it happens
Trigger: setTimeout('console.log(1)', 100) using the browser string form; passing undefined or a non-function variable; passing the result of calling the function -- setTimeout(fn(), 100) -- instead of the reference setTimeout(fn, 100).
Common situations: Copy-pasting browser or Node.js timer code into a k6 script; passing extra arguments expecting them to be forwarded to the callback (not supported); timers configured from external JSON config strings.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- k6/experimental/timers has been graduated, and it's now glob
- a function is expected as the second argument of SharedArray
- csv Parser constructor must be called in the init context
- parsing select option values: %w
- options: expected array, got %T
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/a2eed4dd28bf9175.
Report an issue: GitHub.