grafana/k6 · error

Uncaught (in promise) ${reason}

Error message

Uncaught (in promise) ${reason}

What it means

k6's JS event loop (internal/js/eventloop/eventloop.go:214, newRejectionError) mirrors browser behavior: a promise that rejects with no attached handler is converted into an 'Uncaught (in promise) <reason>' error, failing the iteration/test. The rejectionError wrapper unwraps to the underlying cause error when available.

Source

Thrown at internal/js/eventloop/eventloop.go:214

			}
			return newRejectionError(promise.Result(), value)
		}
	}
}

func newRejectionError(res, v sobek.Value) error {
	var s string
	if v != nil {
		s = v.String()
	}
	// this is the de facto wording in both firefox and deno at least
	msg := "Uncaught (in promise) " + s
	if !common.IsNullish(res) {
		if cause, ok := res.Export().(error); ok {
			return &rejectionError{msg: msg, cause: cause}
		}
	}
	return errors.New(msg)
}

type rejectionError struct {
	msg   string
	cause error
}

func (e *rejectionError) Error() string { return e.msg }
func (e *rejectionError) Unwrap() error { return e.cause }

// WaitOnRegistered waits on all registered callbacks so we know nothing is still doing work.
// This does call back the callbacks and more can be queued over time.
// A different mechanism needs to be used to tell the users that the event loop has errored out or winding down for a
// different reason.
func (e *EventLoop) WaitOnRegistered() {
	for {
		queue, awaiting := e.popAll()
		if len(queue) == 0 {

View on GitHub (pinned to 01ffac6f24)

Solutions

  1. Add `.catch(err => ...)` to every promise chain.
  2. In async functions, `await` promises inside try/catch so rejections surface where you handle them.
  3. Attach a global handler pattern (e.g. wrapping your default function body) so no rejection escapes unobserved.

Example fix

// before
http.async ? null : null;
Promise.reject(new Error('boom')); // unhandled
// after
Promise.reject(new Error('boom')).catch(e => console.error('handled:', e));
Defensive patterns

Strategy: try-catch

Try / catch

// pattern: never let a promise reject unobserved in a VU
export default async function () {
  try {
    await doAsyncWork();
  } catch (err) {
    console.error(`iteration failed: ${err}`); // handle or rethrow deliberately
    throw err;
  }
}
// for fire-and-forget chains, attach:
somePromise().catch(err => console.error('background failure:', err));

Prevention

When it happens

Trigger: Any promise that rejects unhandled: `Promise.reject(...)`, a `.then()` chain without `.catch`, an `async` function invoked without awaiting/catching that throws, unhandled rejections from k6/browser or other async APIs during iteration.

Common situations: Fire-and-forget async calls in VU code; refactoring synchronous calls to async without adding await; error thrown inside setTimeout/setInterval-style callbacks; third-party JS bundles (via webpack) that reject promises internally.

Related errors


AI-assisted analysis of grafana/k6@01ffac6f24 (2026-08-18). Data as JSON: /api/errors/9d01a2575f618808. Report an issue: GitHub.