jackwener/OpenCLI · error · CommandExecutionError

抖音作品列表响应缺少 work_list/aweme_list

Error message

抖音作品列表响应缺少 work_list/aweme_list

What it means

Thrown by findWorkListItem when the work_list API response's candidate lists (data.work_list, aweme_list, work_list) are all absent or the resolved value is not an array. The library needs the list to match aweme_id/item_id against the requested work. Thrown to prevent silently treating a bad response as an empty list.

Source

Thrown at clis/douyin/delete.js:107

          return { ok: false, reason: 'delete_not_confirmed', aweme_id: target.item.aweme_id, item_id: target.item.item_id };
        }
        await sleep(500);
      }
      return { ok: false, reason: 'card_not_found', aweme_id: target.item.aweme_id, item_id: target.item.item_id, index: target.index, listCount: target.listCount };
    })()
  `), '抖音后台管理删除响应异常');

    if (!result?.ok) {
        throw new CommandExecutionError(`抖音后台管理删除失败: ${JSON.stringify(result)}`);
    }
    return result;
}

async function findWorkListItem(page, workId) {
    const data = await browserFetch(page, 'GET', `https://creator.douyin.com${WORK_LIST_URL}`, { timeoutMs: 8000 });
    const list = data.data?.work_list ?? data.aweme_list ?? data.work_list ?? [];
    if (!Array.isArray(list)) {
        throw new CommandExecutionError('抖音作品列表响应缺少 work_list/aweme_list');
    }
    return list.find((entry) => String(entry.aweme_id || '') === workId || String(entry.item_id || '') === workId) || null;
}

cli({
    site: 'douyin',
    name: 'delete',
    access: 'write',
    description: '删除作品(优先使用创作者后台作品管理;找不到时回退到旧删除接口)',
    domain: 'creator.douyin.com',
    strategy: Strategy.COOKIE,
    siteSession: 'persistent',
    args: [
        { name: 'aweme_id', required: true, positional: true, help: '作品 ID / item_id' },
    ],
    columns: ['status'],
    func: async (page, kwargs) => {
        const awemeId = readAwemeId(kwargs.aweme_id);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log the full response to see the actual shape and add the new path to the fallback chain
  2. Re-authenticate (run the login flow) and retry — most often a session problem
  3. Check status=0/count params in WORK_LIST_URL still match the current API
  4. If the new shape nests the array (e.g. data.data.work_list.items), update the extraction

Example fix

// before
const list = data.data?.work_list ?? data.aweme_list ?? data.work_list ?? [];
// after
const list = data.data?.work_list ?? data.work_list ?? data.aweme_list ?? data.data?.aweme_list ?? [];
if (!Array.isArray(list)) throw new Error('shape: ' + JSON.stringify(data).slice(0, 300));
Defensive patterns

Strategy: type-guard

Validate before calling

const data = await browserFetch(page, 'GET', listUrl);
const list = data.data?.work_list ?? data.aweme_list ?? data.work_list;
if (!Array.isArray(list) || list.length === 0) throw new Error('unexpected work list shape: ' + JSON.stringify(data).slice(0, 300));

Type guard

function extractWorkList(data) {
  const l = data?.data?.work_list ?? data?.aweme_list ?? data?.work_list;
  return Array.isArray(l) ? l : null;
}

Try / catch

try {
  const item = await findWorkListItem(page, awemeId);
} catch (e) {
  if (/缺少 work_list/.test(e.message)) {
    await reLoginIfNeeded(page); // most common cause is expired session
    return findWorkListItem(page, awemeId);
  } throw e;
}

Prevention

When it happens

Trigger: Session expired so the API returns an error envelope instead of a list; Douyin renames or nests work_list in the response; risk-control returns HTML/JSON error with 200 status; network truncation yields partial JSON.

Common situations: Running delete right after cookies expired; Douyin API version drift after a backend update; account state (e.g. restricted) causing error payloads.

Related errors


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