jackwener/OpenCLI · error · CommandExecutionError

Malformed toutiao recommend row: missing group_id and articl

Error message

Malformed toutiao recommend row: missing group_id and article source_url.

What it means

buildGroupIdentity derives a group id for a recommend row from either an explicit group_id or the article id embedded in the source_url path (/group/<id>/ or /article/<id>/). When both are absent (or the URL itself was unusable), no identity can be built, so it throws this CommandExecutionError — the row cannot be deduplicated or linked.

Source

Thrown at clis/toutiao/utils.js:228

        }
        return url;
    } catch (error) {
        if (error instanceof CommandExecutionError) throw error;
        throw new CommandExecutionError(`Malformed toutiao recommend row: invalid source_url ${JSON.stringify(raw)}.`);
    }
}

function articleIdFromToutiaoPath(pathname) {
    return String(pathname || '').match(/^\/(?:group|article)\/([A-Za-z0-9_-]+)\/?$/)?.[1] || '';
}

function buildGroupIdentity(sourceUrl, rawGroupId) {
    const source = firstPartyToutiaoUrl(sourceUrl);
    const sourceId = source ? articleIdFromToutiaoPath(source.pathname) : '';
    const explicitId = trimOrNull(rawGroupId);
    const groupId = explicitId || trimOrNull(sourceId);
    if (!groupId) {
        throw new CommandExecutionError('Malformed toutiao recommend row: missing group_id and article source_url.');
    }
    if (!/^[A-Za-z0-9_-]+$/.test(groupId)) {
        throw new CommandExecutionError(`Malformed toutiao recommend row: invalid group_id ${JSON.stringify(rawGroupId)}.`);
    }
    if (sourceId && explicitId && explicitId !== sourceId) {
        throw new CommandExecutionError(`Malformed toutiao recommend row: group_id/source_url mismatch for ${JSON.stringify(rawGroupId)}.`);
    }
    return {
        groupId,
        url: `https://www.toutiao.com/group/${groupId}/`,
    };
}

function formatBehotTime(value) {
    const seconds = Number(value);
    if (!Number.isFinite(seconds) || seconds <= 0) return null;
    return `${new Date(seconds * 1000).toISOString().slice(0, 19)}Z`;
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Ensure each row carries either a group_id or a toutiao.com /group/<id>/ or /article/<id>/ source_url.
  2. Skip rows lacking both identifiers and log them instead of failing the whole run.
  3. If the URL is a non-article toutiao page, extract the id with your own path rule before calling.
  4. Check for upstream API changes — a renamed field (e.g. group_id -> item_id) needs a mapping update.

Example fix

// before
const identity = buildGroupIdentity(row.source_url, row.group_id);
// after
if (!row.group_id && !/\/group|article\/\w+/.test(row.source_url || '')) return skip(row);
const identity = buildGroupIdentity(row.source_url, row.group_id);
Defensive patterns

Strategy: validation

Validate before calling

function hasGroupIdentity(row) {
  const m = String(row.source_url || '').match(/^\/(?:group|article)\/([A-Za-z0-9_-]+)\/?$/) || String(row.source_url || '').replace(/^https:\/\/[^/]+/, '').match(/^\/(?:group|article)\/([A-Za-z0-9_-]+)\/?$/);
  return Boolean(String(row.group_id || '').trim() || (m && m[1]));
}
if (!hasGroupIdentity(row)) skipRow(row);

Type guard

const hasIdentity = (row) => !!(String(row?.group_id || '').trim() || /\/(?:group|article)\/[A-Za-z0-9_-]+\/?$/.test(String(row?.source_url || '').replace(/^https?:\/\/[^/]+/, '')));

Try / catch

try {
  const identity = buildGroupIdentity(row.source_url, row.group_id);
} catch (e) {
  if (e instanceof CommandExecutionError && e.message.includes('missing group_id')) return skipRow(row);
  throw e;
}

Prevention

When it happens

Trigger: A recommend row with no group_id field AND a source_url that is null, empty, off-domain, or a toutiao.com URL whose pathname is not /group/<id>/ or /article/<id>/ (e.g. a homepage or topic URL).

Common situations: Feed rows for non-article content (videos, user pages, special topics) lacking ids; scraping a page where group_id was renamed or moved; fixtures missing the id fields entirely.

Understand the failure class

Related errors


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