jackwener/OpenCLI · error · ArgumentError

douyin stats aweme_id must be a 16-20 digit numeric ID

Error message

douyin stats aweme_id must be a 16-20 digit numeric ID

What it means

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.

Source

Thrown at clis/douyin/stats.js:15

import { cli, Strategy } from '@jackwener/opencli/registry';
import { ArgumentError, EmptyResultError } from '@jackwener/opencli/errors';
import { browserFetch } from './_shared/browser-fetch.js';

// The creator item list is where the per-work metric set lives: play, completion,
// 2s bounce, cover impressions/CTR, fan-vs-visitor split and follow conversion,
// 26 fields in total. It is cursor-paginated the same way work_list is.
const ITEM_LIST_URL = 'https://creator.douyin.com/web/api/creator/item/list';
const PAGE_SIZE = 50;
const MAX_HOPS = 50;

export function normalizeAwemeId(raw) {
    const value = String(raw ?? '').trim();
    if (!/^\d{16,20}$/.test(value)) {
        throw new ArgumentError('douyin stats aweme_id must be a 16-20 digit numeric ID');
    }
    return value;
}

export function sameAwemeId(value, target) {
    if (value == null)
        return false;
    const source = String(value);
    if (source === target)
        return true;
    // This endpoint serializes the work id as a JSON number, so the browser's
    // JSON.parse has already rounded it to IEEE-754 precision before the adapter
    // sees it. Compare numerically as well so the lookup still resolves.
    return /^\d+$/.test(source) && Number(source) === Number(target);
}

cli({
    site: 'douyin',

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Extract the numeric ID from the video URL path (/video/<16-20 digits>) and pass only that.
  2. Resolve v.douyin.com short links first (follow the redirect) to get the canonical /video/<id> URL.
  3. Validate before calling: /^\d{16,20}$/.test(id.trim()).
  4. Confirm you are not passing the author's sec_uid or a comment ID.

Example fix

// before
const id = 'https://v.douyin.com/iRxH123/'
await run(['douyin', 'stats', id]);
// after
const raw = 'https://v.douyin.com/iRxH123/';
const resolved = await resolveShortLink(raw);            // follow redirect
const id = resolved.match(/\/video\/(\d{16,20})/)?.[1];
if (!/^\d{16,20}$/.test(id)) throw new Error(`bad aweme_id: ${id}`);
await run(['douyin', 'stats', id]);
Defensive patterns

Strategy: validation

Validate before calling

function assertAwemeId(raw) {
  const v = String(raw ?? '').trim();
  if (!/^\d{16,20}$/.test(v)) throw new Error(`aweme_id must be 16-20 digits, got: ${v}`);
  return v;
}

Type guard

const isAwemeId = (v) => typeof String(v).trim() === 'string' && /^\d{16,20}$/.test(String(v).trim());

Try / catch

import { ArgumentError } from '@jackwener/opencli/errors';
try {
  await run(['douyin', 'stats', id]);
} catch (e) {
  if (e instanceof ArgumentError) {
    console.error(`Bad aweme_id "${id}" — pass only the numeric ID from /video/<id>`);
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/4779901f1ad25ddc. Report an issue: GitHub.