{"record":{"id":"7490b282f38838e7","repo":"jackwener/OpenCLI","slug":"favorites-button-not-found-make-sure-you-are-log","errorCode":null,"errorMessage":"Favorites button not found - make sure you are logged in","messagePattern":"Favorites button not found - make sure you are logged in","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"clis/tiktok/save.js","lineNumber":18,"sourceCode":"import { cli } from '@jackwener/opencli/registry';\ncli({\n    site: 'tiktok',\n    name: 'save',\n    access: 'write',\n    description: 'Add a TikTok video to Favorites',\n    domain: 'www.tiktok.com',\n    args: [\n        { name: 'url', required: true, positional: true, help: 'TikTok video URL' },\n    ],\n    columns: ['status', 'url'],\n    pipeline: [\n        { navigate: { url: '${{ args.url }}', settleMs: 6000 } },\n        { evaluate: `(async () => {\n  const url = \\${{ args.url | json }};\n  const btn = document.querySelector('[data-e2e=\"bookmark-icon\"]') ||\n              document.querySelector('[data-e2e=\"collect-icon\"]');\n  if (!btn) throw new Error('Favorites button not found - make sure you are logged in');\n  const container = btn.closest('button') || btn.closest('[role=\"button\"]') || btn;\n  const aria = (container.getAttribute('aria-label') || '').toLowerCase();\n  if (aria.includes('remove from favorites') || aria.includes('取消收藏')) {\n    return [{ status: 'Already in Favorites', url: url }];\n  }\n  container.click();\n  await new Promise(r => setTimeout(r, 2000));\n  return [{ status: 'Added to Favorites', url: url }];\n})()\n` },\n    ],\n});\n","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/tiktok/save.js#L1-L31","documentation":"The save-to-favorites script looks for the bookmark button via [data-e2e=\"bookmark-icon\"] or [data-e2e=\"collect-icon\"] and throws if neither exists, telling you to make sure you are logged in. The button only renders for authenticated users viewing an eligible video, so its absence usually means an auth or rendering problem rather than a logic bug.","triggerScenarios":"Calling the save command with a URL whose page never rendered the bookmark button: not logged in, video unavailable/deleted/region-blocked, page still loading past the 6s settle, or TikTok changed the data-e2e attribute names.","commonSituations":"Stale/expired session cookies; running headless without login state; using a link format (e.g. short vm.tiktok.com link) that lands on an interstitial; TikTok A/B test renaming data-e2e attributes.","solutions":["Log into TikTok in the automation browser and persist cookies, then retry","Increase the wait/settle time so the video page fully renders before querying the button","Open the URL manually and inspect whether [data-e2e=\"bookmark-icon\"] or [data-e2e=\"collect-icon\"] exists; if not, the selectors need updating","Verify the video URL resolves to a playable video page, not an interstitial or removed content"],"exampleFix":"// before\nawait run('tiktok save', { url: 'https://vm.tiktok.com/XYZ/' });\n// after\nawait run('tiktok save', { url: 'https://www.tiktok.com/@user/video/1234567890' }); // resolved, logged-in session","handlingStrategy":"validation","validationCode":"await page.goto(url, { waitUntil: 'networkidle' });\nawait page.waitForSelector('[data-e2e=\"bookmark-icon\"], [data-e2e=\"collect-icon\"]', { timeout: 10000 })\n  .catch(() => { throw new Error('Bookmark button never rendered — check login and URL'); });","typeGuard":"function bookmarkButtonExists(doc) {\n  return Boolean(doc.querySelector('[data-e2e=\"bookmark-icon\"]') || doc.querySelector('[data-e2e=\"collect-icon\"]'));\n}","tryCatchPattern":"try {\n  await run('tiktok save', { url });\n} catch (e) {\n  if (/Favorites button not found/.test(e.message)) {\n    console.error('Not logged in or page did not render the bookmark button; verify session and URL');\n  } else {\n    throw e;\n  }\n}","preventionTips":["Always run with a persisted, logged-in TikTok session for favorite actions","Use canonical video URLs, not short share links","Wait for network idle or the selector itself rather than a fixed settle time","Periodically verify data-e2e attribute names against a live page"],"tags":["tiktok","scraping","selector-not-found","auth","dom"],"backgroundTag":"dom-element-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}