jackwener/OpenCLI · error · Error

Favorites button not found - make sure you are logged in

Error message

Favorites button not found - make sure you are logged in

What it means

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.

Source

Thrown at clis/tiktok/save.js:18

import { cli } from '@jackwener/opencli/registry';
cli({
    site: 'tiktok',
    name: 'save',
    access: 'write',
    description: 'Add a TikTok video to Favorites',
    domain: 'www.tiktok.com',
    args: [
        { name: 'url', required: true, positional: true, help: 'TikTok video URL' },
    ],
    columns: ['status', 'url'],
    pipeline: [
        { navigate: { url: '${{ args.url }}', settleMs: 6000 } },
        { evaluate: `(async () => {
  const url = \${{ args.url | json }};
  const btn = document.querySelector('[data-e2e="bookmark-icon"]') ||
              document.querySelector('[data-e2e="collect-icon"]');
  if (!btn) throw new Error('Favorites button not found - make sure you are logged in');
  const container = btn.closest('button') || btn.closest('[role="button"]') || btn;
  const aria = (container.getAttribute('aria-label') || '').toLowerCase();
  if (aria.includes('remove from favorites') || aria.includes('取消收藏')) {
    return [{ status: 'Already in Favorites', url: url }];
  }
  container.click();
  await new Promise(r => setTimeout(r, 2000));
  return [{ status: 'Added to Favorites', url: url }];
})()
` },
    ],
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log into TikTok in the automation browser and persist cookies, then retry
  2. Increase the wait/settle time so the video page fully renders before querying the button
  3. Open the URL manually and inspect whether [data-e2e="bookmark-icon"] or [data-e2e="collect-icon"] exists; if not, the selectors need updating
  4. Verify the video URL resolves to a playable video page, not an interstitial or removed content

Example fix

// before
await run('tiktok save', { url: 'https://vm.tiktok.com/XYZ/' });
// after
await run('tiktok save', { url: 'https://www.tiktok.com/@user/video/1234567890' }); // resolved, logged-in session
Defensive patterns

Strategy: validation

Validate before calling

await page.goto(url, { waitUntil: 'networkidle' });
await page.waitForSelector('[data-e2e="bookmark-icon"], [data-e2e="collect-icon"]', { timeout: 10000 })
  .catch(() => { throw new Error('Bookmark button never rendered — check login and URL'); });

Type guard

function bookmarkButtonExists(doc) {
  return Boolean(doc.querySelector('[data-e2e="bookmark-icon"]') || doc.querySelector('[data-e2e="collect-icon"]'));
}

Try / catch

try {
  await run('tiktok save', { url });
} catch (e) {
  if (/Favorites button not found/.test(e.message)) {
    console.error('Not logged in or page did not render the bookmark button; verify session and URL');
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/7490b282f38838e7. Report an issue: GitHub.