{"record":{"id":"8158ec39646cac70","repo":"jackwener/OpenCLI","slug":"message-8158ec","errorCode":null,"errorMessage":"${message}","messagePattern":"\\$\\{message\\}","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"error","filePath":"clis/tiktok/utils.js","lineNumber":158,"sourceCode":"\nexport function looksTikTokAuthFailure(message) {\n    return /\\bAUTH_REQUIRED\\b|\\b(auth|captcha|login|log in|permission|unauthori[sz]ed|forbidden)\\b|HTTP\\s+(401|403)\\b/i.test(String(message || ''));\n}\n\nexport function looksTikTokUpstreamFailure(message) {\n    return /\\b(API failed|HTTP\\s+\\d+|invalid JSON|Failed to fetch|network|fetch)\\b/i.test(String(message || ''));\n}\n\nexport function throwTikTokPageContextError(error, { authMessage, emptyPattern, emptyTarget, failureMessage }) {\n    const message = getErrorMessage(error);\n    if (looksTikTokAuthFailure(message)) {\n        throw new AuthRequiredError('tiktok.com', authMessage);\n    }\n    if (looksTikTokUpstreamFailure(message)) {\n        throw new CommandExecutionError(`${failureMessage}: ${message}`);\n    }\n    if (emptyPattern.test(message)) {\n        throw new EmptyResultError(emptyTarget, message);\n    }\n    throw new CommandExecutionError(`${failureMessage}: ${message}`);\n}\n\n// Sentinels emitted by Route 1 (button-walker) IIFEs and mapped here to\n// typed errors. Keeping the strings constant in one place makes the IIFE\n// `throw new Error(...)` callsites greppable and the mapper exhaustive.\nexport const BUTTON_WALKER_SENTINELS = {\n    AUTH_REQUIRED: 'AUTH_REQUIRED',\n    BUTTON_NOT_FOUND: 'BUTTON_NOT_FOUND',\n    STATE_VERIFY_FAIL: 'STATE_VERIFY_FAIL',\n    RATE_LIMITED: 'RATE_LIMITED',\n};\n\n// Retryability for write-class typed errors. Captured in the hint string\n// so the human-readable output makes the retry contract explicit and\n// downstream agents/scripts can grep for `retryable=true|false`.\n//","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/tiktok/utils.js#L140-L176","documentation":"The catch-all branch: if the message is neither an auth failure, an upstream failure, nor matched by the caller's emptyPattern, it is wrapped as CommandExecutionError('<failureMessage>: <message>') — identical text to the upstream branch but reached via the fallthrough path.","triggerScenarios":"Page-context listing fails with an unrecognized message that does not match the auth pattern, the upstream pattern, or the supplied emptyPattern — e.g. unexpected DOM/selector errors, 'button not found', or new TikTok page variants.","commonSituations":"TikTok redesigning page markup so selectors fail; unexpected interstitials (captcha variant not matching upstream patterns); a bug in the caller's emptyPattern regex.","solutions":["Read the wrapped message — it contains the original error text and points to the real cause","Update the CLI/scraper if TikTok changed its page structure","Widen or fix emptyPattern in the calling command if 'empty' cases are misrouted here","Retry once; transient oddities can produce one-off unrecognized messages"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"const isCommandExecutionError = (e) => e?.name === 'CommandExecutionError' || e instanceof CommandExecutionError;","tryCatchPattern":"try { await cli.listUserVideos(u); } catch (e) { console.error('Upstream detail:', e.message); if (isCommandExecutionError(e)) reportToMaintainer(e.message); throw e; }","preventionTips":["Log the full wrapped message — the original cause is embedded after the prefix","Keep the CLI updated for TikTok page-markup changes","Audit emptyPattern regexes in your command configs so empty cases route correctly"],"tags":["scraping","unexpected-response","command-execution"],"backgroundTag":"unexpected-scrape-failure","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}