{"record":{"id":"c5d63cec33836671","repo":"jackwener/OpenCLI","slug":"linkedin-learning-whoami-failed-result-detail","errorCode":null,"errorMessage":"LinkedIn Learning whoami failed: ${result.detail}","messagePattern":"LinkedIn Learning whoami failed: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/linkedin-learning/auth.js","lineNumber":44,"sourceCode":"      const mini = d && d.miniProfile;\n      if (!mini || !mini.publicIdentifier) {\n        return { kind: 'auth', detail: 'LinkedIn /voyager/api/me 200 but miniProfile missing' };\n      }\n      const firstName = (mini.firstName && (mini.firstName.text || mini.firstName)) || '';\n      const lastName = (mini.lastName && (mini.lastName.text || mini.lastName)) || '';\n      return {\n        ok: true,\n        public_id: String(mini.publicIdentifier),\n        plain_id: String(d.plainId || ''),\n        name: String((firstName + ' ' + lastName).trim()),\n      };\n    } catch (e) {\n      return { kind: 'exception', detail: String(e && e.message || e) };\n    }\n  })()`);\n  if (result?.kind === 'auth') throw new AuthRequiredError('linkedin.com', result.detail);\n  if (result?.kind === 'http') throw new CommandExecutionError(`HTTP ${result.httpStatus} from /voyager/api/me`);\n  if (result?.kind === 'exception') throw new CommandExecutionError(`LinkedIn Learning whoami failed: ${result.detail}`);\n  if (!result?.ok) throw new CommandExecutionError(`Unexpected LinkedIn Learning probe: ${JSON.stringify(result)}`);\n  return { public_id: result.public_id, plain_id: result.plain_id, name: result.name };\n}\n\nregisterSiteAuthCommands({\n  site: 'linkedin-learning',\n  domain: 'linkedin.com',\n  loginUrl: 'https://www.linkedin.com/login?session_redirect=%2Flearning%2F',\n  columns: ['public_id', 'plain_id', 'name'],\n  quickCheck: hasLinkedinSessionCookie,\n  verify: verifyLinkedinLearningIdentity,\n  poll: async (page) => {\n    if (!await hasLinkedinSessionCookie(page)) {\n      throw new AuthRequiredError('linkedin.com', 'Waiting for LinkedIn li_at cookie');\n    }\n    return verifyLinkedinLearningIdentity(page);\n  },\n});","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin-learning/auth.js#L26-L62","documentation":"The whoami probe wraps its page-side logic in try/catch and reports failures as { kind:'exception', detail }; the command surfaces this as CommandExecutionError 'LinkedIn Learning whoami failed: <detail>'. It means the page-side JavaScript itself threw (network failure inside the page, undefined variable, JSON parsing issue) rather than returning an auth/http verdict.","triggerScenarios":"page.evaluate callback throws — e.g. fetch to /voyager/api/me rejects (network/TLS error in page), reading response JSON throws, or firstName/lastName fields are absent causing a TypeError.","commonSituations":"LinkedIn changed the /voyager/api/me response shape (renamed fields), so `.included[0]` access throws; navigation interrupted the fetch; LinkedIn served an unexpected HTML interstitial that broke JSON parsing.","solutions":["Read result.detail in the error message to identify the underlying exception (TypeError vs network failure).","Re-run after reloading the LinkedIn Learning page — transient in-page fetch failures resolve on retry.","If it's a shape change, update the probe to defensively read the new /voyager/api/me response structure.","Verify the page is actually on linkedin.com (not an error/interstitial page) before probing."],"exampleFix":"// before\nconst included = json.included[0]; // throws when shape changes\n// after\nconst included = json?.included?.[0];\nif (!included) return { kind: 'auth', detail: 'unexpected /voyager/api/me shape' };","handlingStrategy":"try-catch","validationCode":"// Ensure we are on a real LinkedIn Learning page before evaluating\nif (!page.url().includes('linkedin.com/learning')) await page.goto('https://www.linkedin.com/learning/');","typeGuard":"function isExceptionKindResult(r) { return r?.kind === 'exception' && typeof r.detail === 'string'; }","tryCatchPattern":"try {\n  const identity = await verifyLinkedinLearningIdentity(page);\n} catch (e) {\n  if (e.message.includes('whoami failed')) {\n    await page.reload(); // transient in-page failure\n    return verifyLinkedinLearningIdentity(page);\n  }\n  throw e;\n}","preventionTips":["Read the embedded detail to distinguish TypeError (shape change) from network errors.","Use optional chaining in the probe when reading Voyager response fields.","Reload the page and retry before assuming a code bug.","Pin/track LinkedIn API changes — response shape drift is the top cause."],"tags":["linkedin","page-evaluate","api"],"backgroundTag":"api-response-shape-changed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}