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
- Add `.catch(err => ...)` to every promise chain.
- In async functions, `await` promises inside try/catch so rejections surface where you handle them.
- 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
- Await every async call inside try/catch; never call async functions without await or .catch.
- Add a lint rule (no-floating-promises) when scripts are bundled from TypeScript.
- Remember the error's cause is unwrappable — inspect err.cause for the real rejection reason.
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
- initializing the main module hasn't finished, this is a bug
- exported 'setup' must be a function
- exported 'teardown' must be a function
- no exported functions in script
- open() can't be used with an empty filename
AI-assisted analysis of grafana/k6@01ffac6f24 (2026-08-18).
Data as JSON: /api/errors/9d01a2575f618808.
Report an issue: GitHub.