jackwener/OpenCLI · error · CommandExecutionError

Malformed toutiao recommend row: off-domain source_url ${JSO

Error message

Malformed toutiao recommend row: off-domain source_url ${JSON.stringify(raw)}.

What it means

firstPartyToutiaoUrl normalizes a recommend row's source_url into an absolute URL and asserts it points at toutiao.com over http(s). If the URL parses but the protocol is not http/https or the hostname is not toutiao.com (or a subdomain), it throws this CommandExecutionError because downstream group-identity building requires first-party Toutiao links.

Source

Thrown at clis/toutiao/utils.js:209

];

function pickFeedImage(item) {
    const raw = trimOrNull(item?.image_url) || trimOrNull(item?.middle_image?.url);
    if (!raw) return null;
    // The feed serves protocol-relative image URLs (`//p3.pstatp.com/...`);
    // hot-board already returns absolute ones, so normalise for parity.
    return raw.startsWith('//') ? `https:${raw}` : raw;
}

function firstPartyToutiaoUrl(sourceUrl) {
    const raw = trimOrNull(sourceUrl);
    if (!raw) return null;
    try {
        const url = raw.startsWith('/')
            ? new URL(raw, 'https://www.toutiao.com')
            : new URL(raw);
        if (!/^https?:$/i.test(url.protocol) || !/(^|\.)toutiao\.com$/i.test(url.hostname)) {
            throw new CommandExecutionError(`Malformed toutiao recommend row: off-domain source_url ${JSON.stringify(raw)}.`);
        }
        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) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Filter out rows whose source_url host is not toutiao.com before processing the feed.
  2. Resolve redirects yourself first; if the final target is off-domain, skip the row.
  3. If you own the data source, fix source_url to a www.toutiao.com article/group URL.
  4. Wrap the row parse in try/catch on CommandExecutionError and record the row as skipped.

Example fix

// before
const url = firstPartyToutiaoUrl(row.source_url);
// after
let url = null;
try { url = firstPartyToutiaoUrl(row.source_url); }
catch (e) { skipRow(row, e.message); }
Defensive patterns

Strategy: validation

Validate before calling

function isToutiaoUrl(u) {
  try {
    const url = new URL(u, 'https://www.toutiao.com');
    return /^https?:$/.test(url.protocol) && /(^|\.)toutiao\.com$/i.test(url.hostname);
  } catch { return false; }
}

Type guard

const isToutiaoSource = (raw) => !!raw && (() => { try { const u = new URL(raw.startsWith('/') ? new URL(raw, 'https://www.toutiao.com') : raw); return /^https?:$/i.test(u.protocol) && /(^|\.)toutiao\.com$/i.test(u.hostname); } catch { return false; } })();

Try / catch

try {
  const url = firstPartyToutiaoUrl(row.source_url);
} catch (e) {
  if (e instanceof CommandExecutionError) return skipRow(row, e.message);
  throw e;
}

Prevention

When it happens

Trigger: A recommend feed row whose source_url points to another domain (e.g. https://example.com/article/123, a partner site, or a shortlink service like t.cn) or a non-http scheme (javascript:, ftp:).

Common situations: Toutiao mixing third-party/partner content into the recommend feed; scraped fixtures from a different site; a CDN or redirect domain appearing in the field; test data built with placeholder URLs.

Understand the failure class

Related errors


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