{"record":{"id":"f7c9dc44e077d5e6","repo":"grafana/k6","slug":"getting-frame-title-expected-string-got-t","errorCode":null,"errorMessage":"getting frame title: expected string, got %T","messagePattern":"getting frame title: expected string, got %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/frame.go","lineNumber":1894,"sourceCode":"\n// Timeout will return the default timeout or the one set by the user.\n// It's an internal method not to be exposed as a JS API.\nfunc (f *Frame) Timeout() time.Duration {\n\treturn f.defaultTimeout()\n}\n\n// Title returns the title of the frame.\nfunc (f *Frame) Title() (string, error) {\n\tf.log.Debugf(\"Frame:Title\", \"fid:%s furl:%q\", f.ID(), f.URL())\n\n\tjs := `() => document.title`\n\tv, err := f.Evaluate(js)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"getting frame title: %w\", err)\n\t}\n\ts, ok := v.(string)\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"getting frame title: expected string, got %T\", v)\n\t}\n\n\treturn s, nil\n}\n\n// Type text on the first element found matches the selector.\nfunc (f *Frame) Type(selector, text string, popts *FrameTypeOptions) error {\n\tf.log.Debugf(\"Frame:Type\", \"fid:%s furl:%q sel:%q text:%q\", f.ID(), f.URL(), selector, text)\n\n\tif err := f.typ(selector, text, popts); err != nil {\n\t\treturn fmt.Errorf(\"typing %q in %q: %w\", text, selector, err)\n\t}\n\n\tapplySlowMo(f.ctx)\n\n\treturn nil\n}\n","sourceCodeStart":1876,"sourceCodeEnd":1912,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/frame.go#L1876-L1912","documentation":"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.","triggerScenarios":"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.","commonSituations":"Intermittent, seen on pages that navigate frequently (redirects, meta refresh); almost never reproducible twice in a row on a static page.","solutions":["Wait for the frame to settle (waitForLoadState) and call title() again — the result is transient.","Read defensively: const t = await frame.evaluate(() => document.title).catch(() => '');","Update k6 so the module and bundled chromium match."],"exampleFix":"// before\nconst t = await frame.title(); // intermittent type error\n\n// after\nawait frame.waitForLoadState('load');\nconst t = await frame.title().catch(() => '');","handlingStrategy":"try-catch","validationCode":"await frame.waitForLoadState('load');","typeGuard":"const isStr = (v) => typeof v === 'string';","tryCatchPattern":"try { return await frame.title(); }\ncatch (e) {\n  if (/expected string/.test(e.message)) {\n    return frame.evaluate(() => document.title).catch(() => '');\n  }\n  throw e;\n}","preventionTips":["Read titles after navigation settles","Use evaluate as the fallback read path","Update k6/chromium pairs together"],"tags":["browser","frame","type-assertion","race-condition","navigation"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}