{"record":{"id":"5c365ec5ecf35cf3","repo":"jackwener/OpenCLI","slug":"no-videos-found-for-username-suffix","errorCode":null,"errorMessage":"No videos found for @${username}${suffix}","messagePattern":"No videos found for @(.+?)(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"clis/tiktok/user.js","lineNumber":163,"sourceCode":"        if (authorName !== usernameLower) continue;\n        addVideo(dedup, item, 'search-fallback');\n      }\n    } catch (error) {\n      searchFailure = error instanceof Error ? error.message : String(error);\n      break;\n    }\n  }\n\n  const rows = Array.from(dedup.values())\n    .sort((a, b) => (Number(b.createTime) || 0) - (Number(a.createTime) || 0))\n    .slice(0, limit)\n    .map((row, index) => ({ ...row, index: index + 1 }));\n\n  if (rows.length === 0) {\n    const suffix = primaryFailure || searchFailure\n      ? ' (profile/search API failed: ' + (primaryFailure || searchFailure) + ')'\n      : '';\n    throw new Error('No videos found for @' + username + suffix);\n  }\n  return rows;\n})()\n`;\n}\n\nasync function listUserVideos(page, args) {\n    const username = normalizeUsername(args.username);\n    const limit = requireLimit(args.limit, { fallback: DEFAULT_LIMIT, max: MAX_LIMIT });\n    await page.goto(`https://www.tiktok.com/@${encodeURIComponent(username)}`, { waitUntil: 'load', settleMs: 6000 });\n    let rows;\n    try {\n        rows = await page.evaluate(buildUserScript(username, limit));\n    } catch (error) {\n        throwTikTokPageContextError(error, {\n            authMessage: 'TikTok requires browser access to load user videos',\n            emptyPattern: /No videos found/,\n            emptyTarget: 'tiktok user',","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/tiktok/user.js#L145-L181","documentation":"Inside the generated browser pipeline, after deduplicating videos from the universal bootstrap and profile/search API responses, if no rows remain the script throws this error. The suffix '(profile/search API failed: ...)' distinguishes 'network/API calls failed, so we cannot know' from a genuine 'account with zero videos'.","triggerScenarios":"secUid resolved but the profile feed returned no items; the primary profile API and fallback search API both failed (suffix shows the failure reason); the account genuinely has zero public videos; the feed was truncated to page 0 by rate limiting.","commonSituations":"New or empty TikTok accounts; TikTok throttling anonymous requests so both API sources fail; region-blocked content yielding empty feeds; transient API outages during batch scraping.","solutions":["Read the suffix: if it names a profile/search API failure, fix the underlying API issue (msToken, cookies, rate limits) and retry.","Confirm in a browser whether the account really has zero videos.","Add delays/backoff between requests to avoid throttling that empties the feed.","If scraping an account you expect to have videos, check for region restrictions or a private account."],"exampleFix":"// before: surfaces a bare empty error on transient API failure\nconst videos = await cli.tiktok.user('someuser');\n// after: inspect the message and retry on API-failure suffix\ntry {\n  const videos = await cli.tiktok.user('someuser');\n} catch (e) {\n  if (/profile\\/search API failed/.test(e.message)) await retryWithBackoff(() => cli.tiktok.user('someuser'));\n  else throw e;\n}","handlingStrategy":"retry","validationCode":"// detect genuine emptiness vs transient API failure by checking the account first\nconst page = await fetch(`https://www.tiktok.com/@${handle}`);\nconst html = await page.text();\nconst hasItems = /\"itemList\"\\s*:\\s*\\[\\s*\\{/.test(html);\nif (!hasItems) console.warn(`@${handle} may genuinely have no public videos`);","typeGuard":null,"tryCatchPattern":"try {\n  rows = await cli.tiktok.user(handle);\n} catch (e) {\n  if (/No videos found/.test(e.message) && /API failed/.test(e.message)) {\n    rows = await retryWithBackoff(() => cli.tiktok.user(handle), { attempts: 3 });\n  } else throw e;\n}","preventionTips":["Add throttling/backoff so profile and search APIs are not both rate-limited","Treat the '(profile/search API failed: ...)' suffix as a transient signal, not emptiness","Confirm accounts have public videos before scraping them","Keep fallback APIs (search) authenticated so they can actually contribute rows"],"tags":["tiktok","empty-result","api","rate-limit"],"backgroundTag":"empty-result-set","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}