grafana/k6 · error

getting frame title: expected string, got %T

Error message

getting frame title: expected string, got %T

What it means

Frame.Title() got a successful evaluation result that is not a Go string, so the assertion `v.(string)` fails: 'getting frame title: expected string, got <T>'. document.title is always a string in a healthy page, so a non-string result means the evaluation resolved against a replaced/destroyed context or a protocol-level anomaly.

Source

Thrown at internal/js/modules/k6/browser/common/frame.go:1894

// Timeout will return the default timeout or the one set by the user.
// It's an internal method not to be exposed as a JS API.
func (f *Frame) Timeout() time.Duration {
	return f.defaultTimeout()
}

// Title returns the title of the frame.
func (f *Frame) Title() (string, error) {
	f.log.Debugf("Frame:Title", "fid:%s furl:%q", f.ID(), f.URL())

	js := `() => document.title`
	v, err := f.Evaluate(js)
	if err != nil {
		return "", fmt.Errorf("getting frame title: %w", err)
	}
	s, ok := v.(string)
	if !ok {
		return "", fmt.Errorf("getting frame title: expected string, got %T", v)
	}

	return s, nil
}

// Type text on the first element found matches the selector.
func (f *Frame) Type(selector, text string, popts *FrameTypeOptions) error {
	f.log.Debugf("Frame:Type", "fid:%s furl:%q sel:%q text:%q", f.ID(), f.URL(), selector, text)

	if err := f.typ(selector, text, popts); err != nil {
		return fmt.Errorf("typing %q in %q: %w", text, selector, err)
	}

	applySlowMo(f.ctx)

	return nil
}

View on GitHub (pinned to 93accf6570)

Solutions

  1. Wait for the frame to settle (waitForLoadState) and call title() again — the result is transient.
  2. Read defensively: const t = await frame.evaluate(() => document.title).catch(() => '');
  3. Update k6 so the module and bundled chromium match.

Example fix

// before
const t = await frame.title(); // intermittent type error

// after
await frame.waitForLoadState('load');
const t = await frame.title().catch(() => '');
Defensive patterns

Strategy: try-catch

Validate before calling

await frame.waitForLoadState('load');

Type guard

const isStr = (v) => typeof v === 'string';

Try / catch

try { return await frame.title(); }
catch (e) {
  if (/expected string/.test(e.message)) {
    return frame.evaluate(() => document.title).catch(() => '');
  }
  throw e;
}

Prevention

When it happens

Trigger: Navigation replaced the execution context between evaluate dispatch and result deserialization; the frame was detached mid-call; version skew between k6's browser module and the chromium binary.

Common situations: Intermittent, seen on pages that navigate frequently (redirects, meta refresh); almost never reproducible twice in a row on a static page.

Related errors


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