grafana/k6 · error
unexpected value of type %T
Error message
unexpected value %v of type %T
What it means
The count() path received a non-float64 result from the injected counting script (expected a number returned by value from the evaluation). This is an internal Go/JS contract violation — legitimate DOM counts always arrive as float64 via returnByValue.
Solutions
- Upgrade or pin a consistent k6 version
- Isolate against a static page to rule out app interference
- Work around with page.evaluate(() => document.querySelectorAll(sel).length)
- File a k6 issue with the minimal case
Example fix
// workaround
const n = await page.evaluate(() => document.querySelectorAll('.item').length); Defensive patterns
Strategy: fallback
Try / catch
try {
const n = await page.locator('.item').count();
} catch (e) {
if (/unexpected value/.test(String(e))) {
const n = await page.evaluate(() => document.querySelectorAll('.item').length);
} else throw e;
} Prevention
- Pin k6 versions; report internal invariant errors upstream with a reproducer
When it happens
Trigger: el.count() or frame-chained counts where evalWithScript returns a string/object/undefined because the injected script was interfered with or the evaluation protocol broke; distinct from selector errors, which fail earlier in NewSelector/waitForSelector.
Common situations: Adversarial pages patching globals used by injected scripts; k6 version mismatch between the generated JS helpers and the binary; not seen in ordinary flows.
Related errors
- checking element is in viewport: unexpected type %T
- checking state of element
- unexpected type %T
- waiting for states of element
- value out of range for int32 type
AI-assisted analysis of grafana/k6@3fcf5388d7 (2026-08-18).
Data as JSON: /api/errors/92f542c827d6728b.
Report an issue: GitHub.
Appendix: source
Thrown at internal/js/modules/k6/browser/common/element_handle.go:767
eopts := evalOptions{
forceCallable: true,
returnByValue: true,
}
result, err := h.evalWithScript(
apiCtx,
eopts, fn, parsedSelector,
)
if err != nil {
return 0, err
}
switch r := result.(type) {
case float64:
if r < float64(math.MinInt32) || r > float64(math.MaxInt32) {
return 0, fmt.Errorf("value %v out of range for int32 type", r)
}
return int(r), nil
default:
return 0, fmt.Errorf("unexpected value %v of type %T", r, r)
}
}
// findFrameNavigationIndex finds the index of the first internal:control=enter-frame directive
func (h *ElementHandle) findFrameNavigationIndex(selector *Selector) int {
for i, part := range selector.Parts {
if part.Name == "internal:control" && part.Body == "enter-frame" {
return i
}
}
return -1
}
// splitSelectorAtFrame splits a selector at the frame navigation boundary
func (h *ElementHandle) splitSelectorAtFrame(selector *Selector, frameIndex int) (*Selector, *Selector) {
beforeFrame := &Selector{
Selector: selector.Selector, // Keep original for reference
Parts: selector.Parts[:frameIndex],View on GitHub (pinned to 3fcf5388d7)