grafana/k6 · error

unexpected type %T (expecting string)

Error message

unexpected type %T (expecting string)

What it means

Internal invariant failure inside ElementHandle.innerHTML(): the evaluation succeeded but the returned remote value did not type-assert to a Go string. element.innerHTML is always a string in a live DOM, so a non-string payload points to a protocol/serialization anomaly or a k6 bug rather than script misuse.

Source

Thrown at internal/js/modules/k6/browser/common/element_handle.go:1004

// InnerHTML returns the inner HTML of the element.
func (h *ElementHandle) InnerHTML() (string, error) {
	innerHTML := func(apiCtx context.Context, handle *ElementHandle) (any, error) {
		return handle.innerHTML(apiCtx)
	}
	opts := NewElementHandleBaseOptions(h.DefaultTimeout())
	innerHTMLAction := h.newAction(
		[]string{}, innerHTML, opts.Force, withRetry, opts.NoWaitAfter, opts.Timeout,
	)
	v, err := call(h.ctx, innerHTMLAction, opts.Timeout)
	if err != nil {
		return "", fmt.Errorf("getting element's inner HTML: %w", err)
	}

	applySlowMo(h.ctx)

	s, ok := v.(string)
	if !ok {
		return "", fmt.Errorf("unexpected type %T (expecting string)", v)
	}

	return s, nil
}

// InnerText returns the inner text of the element.
func (h *ElementHandle) InnerText() (string, error) {
	innerText := func(apiCtx context.Context, handle *ElementHandle) (any, error) {
		return handle.innerText(apiCtx)
	}
	opts := NewElementHandleBaseOptions(h.DefaultTimeout())
	innerTextAction := h.newAction(
		[]string{}, innerText, opts.Force, withRetry, opts.NoWaitAfter, opts.Timeout.Abs(),
	)
	v, err := call(h.ctx, innerTextAction, opts.Timeout)
	if err != nil {
		return "", fmt.Errorf("getting element's inner text: %w", err)
	}

View on GitHub (pinned to 93accf6570)

Solutions

  1. Upgrade k6 to the latest release
  2. Pin a Chromium version compatible with your k6 release
  3. Work around with evaluate: const html = await el.evaluate((e) => e.innerHTML)
  4. Report to k6 with script and browser versions if it persists

Example fix

// before
const html = await el.innerHTML();
// after (workaround)
const html = await el.evaluate((e) => e.innerHTML);
if (typeof html !== 'string') throw new Error('innerHTML not a string');
Defensive patterns

Strategy: validation

Validate before calling

const html = await handle.evaluate((e) => e.innerHTML);
if (typeof html !== 'string') {
  throw new Error('innerHTML was not a string');
}

Type guard

function isNonEmptyHtml(v) {
  return typeof v === 'string';
}

Try / catch

try {
  return await handle.innerHTML();
} catch (e) {
  if (String(e).includes('expecting string')) {
    return handle.evaluate((e) => e.innerHTML);
  }
  throw e;
}

Prevention

When it happens

Trigger: Requires the CDP evaluation to return a non-string RemoteObject - essentially only with a Chromium/CDP version mismatched to the k6 release or a k6 regression; not triggerable by normal scripts.

Common situations: Canary/nightly Chromium builds against a stable k6; custom browser executables; k6 versions with known evaluation bugs.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/79057b393a6dfa7f. Report an issue: GitHub.