{"record":{"id":"72a3b9ef07889421","repo":"jackwener/OpenCLI","slug":"twitter-x-profile-link-probe-returned-a-malformed","errorCode":null,"errorMessage":"Twitter/X profile link probe returned a malformed href","messagePattern":"Twitter/X profile link probe returned a malformed href","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/auth.js","lineNumber":31,"sourceCode":"}\n\nfunction unwrapTwitterEvaluateResult(value, label) {\n  if (value && typeof value === 'object' && !Array.isArray(value) && 'session' in value) {\n    if (typeof value.session === 'string' && Object.prototype.hasOwnProperty.call(value, 'data')) {\n      return value.data;\n    }\n    throw new CommandExecutionError(`Twitter/X ${label} returned a malformed Browser Bridge envelope`);\n  }\n  return value;\n}\n\nasync function readScreenName(page) {\n  const href = unwrapTwitterEvaluateResult(await page.evaluate(`() => {\n    const link = document.querySelector('a[data-testid=\"AppTabBar_Profile_Link\"]');\n    return link ? link.getAttribute('href') : null;\n  }`), 'profile link probe');\n  if (href !== null && typeof href !== 'string') {\n    throw new CommandExecutionError('Twitter/X profile link probe returned a malformed href');\n  }\n  return normalizeTwitterScreenName(typeof href === 'string' ? href : '');\n}\n\n/**\n * Right after an account switch the home surface keeps showing the previous\n * account for a few seconds, so a single read misreports it (#2252); trust\n * the handle only once it holds across three polls.\n */\nasync function readSettledScreenName(page) {\n  const samples = [];\n  for (let poll = 0; poll < SCREEN_NAME_POLLS; poll += 1) {\n    samples.push(await readScreenName(page));\n    if (poll < SCREEN_NAME_POLLS - 1) {\n      await page.sleep(SCREEN_NAME_POLL_SECONDS);\n    }\n  }\n  const tail = samples.slice(-SCREEN_NAME_AGREEMENTS);","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/auth.js#L13-L49","documentation":"readScreenName reads the profile link (a[data-testid=\"AppTabBar_Profile_Link\"]) href via page.evaluate and passes it through unwrapTwitterEvaluateResult. If the bridge returns a defined value that is not a string or null, the DOM probe result is deemed malformed and this CommandExecutionError is thrown. Called by readSettledScreenName and username flows.","triggerScenarios":"The href probe returns a non-string, non-null value — e.g. the envelope unwrap yielded a number, object, or array instead of the link's href attribute.","commonSituations":"Browser Bridge envelope corruption (related to 4003), a page where a custom element or extension replaced the profile link attribute type, or Twitter DOM experiments changing what the selector matches.","solutions":["Reload x.com and retry; the selector usually returns a proper href once the page is stable.","Re-authenticate so the logged-in nav bar (which contains the profile link) renders.","Check for conflicting browser extensions that mutate DOM attributes on x.com.","Update the CLI/extension pair if Twitter shipped a DOM change."],"exampleFix":"// before\nconst href = unwrapTwitterEvaluateResult(await page.evaluate(probeSrc), 'profile link probe');\n// after\nconst href = unwrapTwitterEvaluateResult(await page.evaluate(probeSrc), 'profile link probe');\nif (href !== null && typeof href !== 'string') {\n  console.warn('href probe returned', typeof href); // log before throwing\n}","handlingStrategy":"type-guard","validationCode":"await page.waitForSelector('a[data-testid=\"AppTabBar_Profile_Link\"]', { timeout: 5000 }).catch(() => null);","typeGuard":"function isHref(value) {\n  return value === null || typeof value === 'string';\n}","tryCatchPattern":"const href = unwrapTwitterEvaluateResult(await page.evaluate(probeSrc), 'profile link probe');\nif (!isHref(href)) throw new Error('href probe returned ' + typeof href);","preventionTips":["Wait for the logged-in nav bar to render before probing","Log in fully so AppTabBar_Profile_Link exists","Disable other extensions that mutate x.com DOM","Update the CLI when Twitter changes data-testid attributes"],"tags":["dom-probe","type-mismatch","browser-bridge"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}