grafana/k6 · error
unmarshaling wait for load state %q: %w
Error message
unmarshaling wait for load state %q: %w
What it means
Thrown by Frame.WaitForLoadState when the state string cannot be parsed into a LifecycleEvent. waitUntil.UnmarshalText only accepts exactly "load", "domcontentloaded", or "networkidle" (see lifecycle.go's lifecycleEventToID map); anything else fails before any waiting starts. It is a deterministic argument-validation error, not a timing problem.
Source
Thrown at internal/js/modules/k6/browser/common/frame.go:2040
return nil, nil //nolint:nilnil
}
return result, nil
}
// WaitForLoadState waits for the given load state to be reached.
// This will unblock if that lifecycle event has already been received.
func (f *Frame) WaitForLoadState(state string, popts *FrameWaitForLoadStateOptions) error {
f.log.Debugf("Frame:WaitForLoadState", "fid:%s furl:%q state:%s", f.ID(), f.URL(), state)
defer f.log.Debugf("Frame:WaitForLoadState:return", "fid:%s furl:%q state:%s", f.ID(), f.URL(), state)
timeoutCtx, timeoutCancel := context.WithTimeout(f.ctx, popts.Timeout)
defer timeoutCancel()
waitUntil := LifecycleEventLoad
if state != "" {
if err := waitUntil.UnmarshalText([]byte(state)); err != nil {
return fmt.Errorf("unmarshaling wait for load state %q: %w", state, err)
}
}
lifecycleEvent, lifecycleEventCancel := createWaitForEventPredicateHandler(
timeoutCtx,
f,
[]string{EventFrameAddLifecycle},
func(data any) bool {
if le, ok := data.(FrameLifecycleEvent); ok {
return le.Event == waitUntil
}
return false
})
defer lifecycleEventCancel()
if f.hasLifecycleEventFired(waitUntil) {
return nil
}View on GitHub (pinned to 93accf6570)
Solutions
- Use exactly one of: 'load', 'domcontentloaded', 'networkidle' (lowercase, no spaces)
- If the state comes from configuration, validate it against the allowed set before passing it
Example fix
// before
page.waitForLoadState('network idle');
// after
page.waitForLoadState('networkidle'); Defensive patterns
Strategy: validation
Validate before calling
const STATES = ['load', 'domcontentloaded', 'networkidle'];
function assertLoadState(state) {
if (!STATES.includes(state)) throw new Error(`invalid state ${state}; use ${STATES.join('|')}`);
}
assertLoadState(cfg.state); Type guard
function isValidLoadState(s) {
return s === '' || ['load', 'domcontentloaded', 'networkidle'].includes(s);
} Prevention
- Validate state strings at config load time, not at call time
- Keep lifecycle constants in one shared object in the test suite
When it happens
Trigger: page.waitForLoadState('loaded'), page.waitForLoadState('Load'), page.waitForLoadState('network idle'), or a state value built from a variable/typo that is not one of the three accepted strings; an empty string is fine (defaults to load), but any other misspelling hits this path.
Common situations: Copy-pasting Playwright examples with informal state names; case mismatches; passing a waitUntil value configured for goto (where the same enum is used) into waitForLoadState without validation.
Related errors
- waiting for load state %q: %w
- %q is not a valid lifecycle
- exported 'setup' must be a function
- exported 'teardown' must be a function
- predicate function is not callable
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/930b52e22e71a486.
Report an issue: GitHub.