{"record":{"id":"4779901f1ad25ddc","repo":"jackwener/OpenCLI","slug":"douyin-stats-aweme-id-must-be-a-16-20-digit-numeri","errorCode":null,"errorMessage":"douyin stats aweme_id must be a 16-20 digit numeric ID","messagePattern":"douyin stats aweme_id must be a 16-20 digit numeric ID","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/douyin/stats.js","lineNumber":15,"sourceCode":"import { cli, Strategy } from '@jackwener/opencli/registry';\nimport { ArgumentError, EmptyResultError } from '@jackwener/opencli/errors';\nimport { browserFetch } from './_shared/browser-fetch.js';\n\n// The creator item list is where the per-work metric set lives: play, completion,\n// 2s bounce, cover impressions/CTR, fan-vs-visitor split and follow conversion,\n// 26 fields in total. It is cursor-paginated the same way work_list is.\nconst ITEM_LIST_URL = 'https://creator.douyin.com/web/api/creator/item/list';\nconst PAGE_SIZE = 50;\nconst MAX_HOPS = 50;\n\nexport function normalizeAwemeId(raw) {\n    const value = String(raw ?? '').trim();\n    if (!/^\\d{16,20}$/.test(value)) {\n        throw new ArgumentError('douyin stats aweme_id must be a 16-20 digit numeric ID');\n    }\n    return value;\n}\n\nexport function sameAwemeId(value, target) {\n    if (value == null)\n        return false;\n    const source = String(value);\n    if (source === target)\n        return true;\n    // This endpoint serializes the work id as a JSON number, so the browser's\n    // JSON.parse has already rounded it to IEEE-754 precision before the adapter\n    // sees it. Compare numerically as well so the lookup still resolves.\n    return /^\\d+$/.test(source) && Number(source) === Number(target);\n}\n\ncli({\n    site: 'douyin',","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douyin/stats.js#L1-L33","documentation":"ArgumentError from normalizeAwemeId: the positional aweme_id passed to `douyin stats` is not a 16-20 digit numeric string. Douyin work IDs are large numeric tokens taken from the end of a video URL (https://www.douyin.com/video/<aweme_id>), and the creator metrics API requires the exact ID.","triggerScenarios":"Passing a share-link short code (e.g. v.douyin.com/xxxx), a URL instead of the bare ID, a numeric ID with fewer than 16 digits or extra characters, whitespace-padded/non-numeric input, or null/undefined when the arg is omitted programmatically.","commonSituations":"Copying the whole video URL instead of the trailing ID; using the sec_uid or author id by mistake; shell quoting stripping digits; extracting the ID with a regex that catches the wrong group.","solutions":["Extract the numeric ID from the video URL path (/video/<16-20 digits>) and pass only that.","Resolve v.douyin.com short links first (follow the redirect) to get the canonical /video/<id> URL.","Validate before calling: /^\\d{16,20}$/.test(id.trim()).","Confirm you are not passing the author's sec_uid or a comment ID."],"exampleFix":"// before\nconst id = 'https://v.douyin.com/iRxH123/'\nawait run(['douyin', 'stats', id]);\n// after\nconst raw = 'https://v.douyin.com/iRxH123/';\nconst resolved = await resolveShortLink(raw);            // follow redirect\nconst id = resolved.match(/\\/video\\/(\\d{16,20})/)?.[1];\nif (!/^\\d{16,20}$/.test(id)) throw new Error(`bad aweme_id: ${id}`);\nawait run(['douyin', 'stats', id]);","handlingStrategy":"validation","validationCode":"function assertAwemeId(raw) {\n  const v = String(raw ?? '').trim();\n  if (!/^\\d{16,20}$/.test(v)) throw new Error(`aweme_id must be 16-20 digits, got: ${v}`);\n  return v;\n}","typeGuard":"const isAwemeId = (v) => typeof String(v).trim() === 'string' && /^\\d{16,20}$/.test(String(v).trim());","tryCatchPattern":"import { ArgumentError } from '@jackwener/opencli/errors';\ntry {\n  await run(['douyin', 'stats', id]);\n} catch (e) {\n  if (e instanceof ArgumentError) {\n    console.error(`Bad aweme_id \"${id}\" — pass only the numeric ID from /video/<id>`);\n  }\n  throw e;\n}","preventionTips":["Always extract the trailing numeric segment of the canonical /video/<id> URL.","Resolve v.douyin.com short links before use.","Do not confuse aweme_id with sec_uid, author id, or comment id.","Validate with /^\\d{16,20}$/ in shell wrappers before invoking the CLI."],"tags":["validation","argument-error","douyin","input"],"backgroundTag":"invalid-argument-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}