{"record":{"id":"1c6e2e96f1b9449a","repo":"jackwener/OpenCLI","slug":"username-has-no-media","errorCode":null,"errorMessage":"@${username} has no media","messagePattern":"@(.+?) has no media","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"warning","filePath":"clis/twitter/download.js","lineNumber":408,"sourceCode":"        try {\n          const r = await fetch(\"${url}\", { headers: ${headers}, credentials: 'include' });\n          if (!r.ok) return { ok: false, status: r.status };\n          return { ok: true, payload: await r.json() };\n        } catch (err) {\n          return { ok: false, error: err?.message ?? String(err) };\n        }\n        }`)));\n        const { items, nextCursor } = parseUserMedia(data, seen);\n        all.push(...items);\n        hasMorePages = Boolean(nextCursor);\n        if (!nextCursor) break;\n        if (nextCursor === cursor) {\n            throw new CommandExecutionError('Twitter UserMedia pagination returned the same cursor twice');\n        }\n        cursor = nextCursor;\n    }\n\n    if (all.length === 0) throw new EmptyResultError(`@${username} has no media`, 'Account may be private, suspended, or have no media posts');\n    if (all.length < limit && hasMorePages) {\n        throw new CommandExecutionError(`Twitter UserMedia pagination reached the ${MAX_PAGINATION_PAGES}-page safety cap before collecting ${limit} media items`);\n    }\n\n    const trimmed = all.slice(0, limit);\n    return downloadTwitterMedia(trimmed, {\n        output,\n        subdir: username,\n        cookies: formatCookieHeader(cookies),\n        browserCookies: cookies,\n        filenamePrefix: username,\n        ytdlpExtraArgs: ['--merge-output-format', 'mp4'],\n    });\n}\n\nasync function downloadSingleTweet(page, tweetUrl, output) {\n    const target = parseTweetUrl(tweetUrl);\n    await page.goto(target.url);","sourceCodeStart":390,"sourceCodeEnd":426,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/download.js#L390-L426","documentation":"After pagination completes with zero collected media items, the command throws EmptyResultError `@<username> has no media` with detail noting the account may be private, suspended, or simply have no media posts. This distinguishes a legitimately empty result from a failure.","triggerScenarios":"The target account exists (rest_id resolved) but every parsed UserMedia page yields zero media entities: account with no tweets containing media, protected account visible only to followers, suspended mid-flow, or parseUserMedia filters dropping everything.","commonSituations":"Querying a text-only account expecting images/videos; following a private account from a session that isn't authorized; account got suspended between lookup and media fetch; media parse logic outdated after x.com UI/GraphQL changes.","solutions":["Verify the account has media posts by browsing https://x.com/<username>/media","Ensure your browser session follows/can view the account if it is private","If media visibly exists but none is found, update the CLI so parseUserMedia matches current response shape"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const hasMedia = await checkProfileMediaTab(username); // visit x.com/<u>/media first\nif (!hasMedia) {\n  console.log('Account has no downloadable media; skipping');\n  return [];\n}","typeGuard":null,"tryCatchPattern":"try {\n  await cmd();\n} catch (err) {\n  if (err.name === 'EmptyResultError') {\n    return []; // legitimately no media — treat as success in pipelines\n  }\n  throw err;\n}","preventionTips":["Check https://x.com/<username>/media in a browser before bulk runs","Ensure your session can view the account if it is private","Treat EmptyResultError as an empty result, not a hard failure, in automation"],"tags":["empty-result","twitter","media"],"backgroundTag":"empty-result-set","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}