{"record":{"id":"e1a593a73ca8431e","repo":"jackwener/OpenCLI","slug":"douyin-search-results-are-blocked-behind-a-login-w","errorCode":null,"errorMessage":"Douyin search results are blocked behind a login wall — log in at https://www.douyin.com in Chrome first.","messagePattern":"Douyin search results are blocked behind a login wall — log in at https://www\\.douyin\\.com in Chrome first\\.","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/douyin/search.js","lineNumber":282,"sourceCode":"    columns: ['rank', 'desc', 'author', 'url', 'plays', 'likes', 'comments', 'shares'],\n    func: async (page, kwargs) => {\n        const limit = parseSearchLimit(kwargs.limit);\n        const keyword = String(kwargs.query ?? '').trim();\n        if (!keyword) {\n            throw new ArgumentError('douyin search 需要 <query> 关键词');\n        }\n        await page.goto(`https://www.douyin.com/search/${encodeURIComponent(keyword)}?type=video`);\n        let result;\n        try {\n            result = unwrapEvaluateResult(await page.evaluate(WAIT_AND_EXTRACT_JS(RENDER_TIMEOUT_MS)));\n        } catch (error) {\n            throw new CommandExecutionError(`Douyin search extraction failed: ${error instanceof Error ? error.message : String(error)}`);\n        }\n        if (!result || typeof result !== 'object') {\n            throw new CommandExecutionError('Douyin search: unexpected evaluator payload shape');\n        }\n        if (result.state === 'login_wall') {\n            throw new AuthRequiredError(\n                'www.douyin.com',\n                'Douyin search results are blocked behind a login wall — log in at https://www.douyin.com in Chrome first.',\n            );\n        }\n        if (result.state === 'empty') {\n            throw new EmptyResultError('douyin search', `No Douyin videos matched \"${keyword}\".`);\n        }\n        if (result.state === 'timeout') {\n            throw new CommandExecutionError('Douyin search did not render result cards within the timeout. Open the same search in Chrome and verify login/security state before retrying.');\n        }\n        if (!Array.isArray(result.cards)) {\n            throw new CommandExecutionError('Douyin search: evaluator returned malformed cards payload');\n        }\n        if (result.cards.length === 0) {\n            throw new EmptyResultError('douyin search', `No Douyin videos matched \"${keyword}\".`);\n        }\n        const projected = projectSearchCards(result.cards, limit);\n        if (projected.invalidCount > 0) {","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douyin/search.js#L264-L300","documentation":"AuthRequiredError thrown when the Douyin search page's rendered DOM reports state === 'login_wall'. The search results page renders an empty/login-gated skeleton for visitors without an authenticated session, so the adapter refuses to pretend there are zero results and asks the user to log in via the bound Chrome profile.","triggerScenarios":"Running `douyin search` while the bound Chrome profile is logged out of www.douyin.com, the session cookie expired, Douyin invalidated the session (security check), or the wrong Chrome profile (without a Douyin login) is bound.","commonSituations":"Fresh machine/CI container where Chrome was never logged in; Douyin rotating sessions after risk control; cookie purge from clearing browser data; using a headless or brand-new profile.","solutions":["Open https://www.douyin.com in the bound Chrome profile and log in manually, then retry the command.","Confirm the CLI is attached to the intended Chrome profile (not a fresh/incognito one).","If already logged in, complete any Douyin security/captcha verification in the browser and retry.","Re-login to refresh expired cookies, then re-run."],"exampleFix":"// no code fix; but callers can branch on the error type\n// before\nclassifiedCLI(args).catch(e => { throw e; });\n// after\nimport { AuthRequiredError } from '@jackwener/opencli/errors';\ntry {\n  return await douyinSearch(keyword);\n} catch (e) {\n  if (e instanceof AuthRequiredError) {\n    console.error('Log in to douyin.com in Chrome, then retry');\n    return [];\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// before running: check login state in the bound profile\nconst loggedIn = await page.evaluate(() => !!document.querySelector('[data-e2e]') && document.cookie.includes('sessionid'));\nif (!loggedIn) throw new Error('Log in to douyin.com in Chrome first');","typeGuard":null,"tryCatchPattern":"import { AuthRequiredError } from '@jackwener/opencli/errors';\ntry {\n  rows = await douyinSearch(keyword);\n} catch (e) {\n  if (e instanceof AuthRequiredError) {\n    console.error(`Auth required for ${e.domain ?? 'douyin'}: ${e.message}`);\n    process.exitCode = 3;\n    return;\n  }\n  throw e;\n}","preventionTips":["Log in to www.douyin.com in the bound Chrome profile before scripting.","Re-login periodically; Douyin sessions expire and get invalidated by risk control.","Never point the CLI at an incognito or throwaway Chrome profile."],"tags":["authentication","login-wall","douyin","cookies"],"backgroundTag":"site-login-required","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}