jackwener/OpenCLI · error · CommandExecutionError

Pixiv bookmark archive contains a duplicate download target:

Error message

Pixiv bookmark archive contains a duplicate download target: ${target}

What it means

After building a download plan for every bookmark row, commitIllustPlan's caller ensures no two plans target the same destination path (novels use plan.destPath, illustrations use plan.finalPath). This CommandExecutionError is thrown when the Set of targets detects a duplicate, because two files would race to write the same path and one would clobber the other.

Source

Thrown at clis/pixiv/bookmark-download.js:181

    if (rows.length === 0) {
      throw new EmptyResultError('pixiv bookmark-download', 'No Pixiv bookmarks matched the requested page.');
    }

    // Complete every API/schema/collision check before creating a file.
    const plans = [];
    for (const row of rows) {
      if (type === 'novel') {
        const body = await fetchNovelForDownload(page, row.novel_id);
        plans.push({ ...prepareNovelFile(body, path.join(outputRoot, 'novel'), format), row });
      } else {
        plans.push({ ...await prepareIllustPlan(page, row, outputRoot), row });
      }
    }
    const targets = new Set();
    for (const plan of plans) {
      const target = plan.kind === 'novel' ? plan.destPath : plan.finalPath;
      if (targets.has(target)) {
        throw new CommandExecutionError(`Pixiv bookmark archive contains a duplicate download target: ${target}`);
      }
      targets.add(target);
    }

    let cookies = '';
    if (type === 'illust') {
      let rawCookies;
      try {
        rawCookies = await page.getCookies({ domain: 'pixiv.net' });
      } catch (error) {
        throw new CommandExecutionError(`Pixiv cookie lookup failed: ${error?.message || error}`);
      }
      if (!Array.isArray(rawCookies)) {
        throw new CommandExecutionError('Pixiv cookie lookup returned malformed data');
      }
      try {
        cookies = formatCookieHeader(rawCookies);
      } catch (error) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Deduplicate rows by illustId/novelId before building plans
  2. Include a unique component (illust id, page index) in destPath/finalPath generation so distinct items never collide
  3. Remove duplicate bookmarks on Pixiv, or restrict the page range so rows don't overlap
  4. Check whether a custom --output naming option causes collisions and adjust it

Example fix

// before: plans may contain dupes
const plans = rows.map(buildPlan);
// after: dedupe by id first
const seen = new Set();
const plans = rows.filter(r => !seen.has(r.id) && seen.add(r.id)).map(buildPlan);
Defensive patterns

Strategy: validation

Validate before calling

const targets = new Set();
for (const plan of plans) {
  const t = plan.kind === 'novel' ? plan.destPath : plan.finalPath;
  if (targets.has(t)) throw new Error(`duplicate target ${t}`);
  targets.add(t);
}

Try / catch

try {
  await runBookmarkArchive(rows, kwargs);
} catch (err) {
  if (/duplicate download target/.test(err.message)) {
    const dup = err.message.split(': ')[1];
    console.warn(`Deduplicating and retrying without ${dup}`);
    return runBookmarkArchive(dedupeRows(rows), kwargs);
  }
  throw err;
}

Prevention

When it happens

Trigger: Two or more rows on the requested bookmark page(s) resolve to the same download target — e.g. the same artwork bookmarked/appearing twice, or filename generation colliding for two different artworks (same id+filename derivation).

Common situations: A bookmark page listing the same illust twice (e.g. via different bookmark tags); output naming scheme too coarse (filename omits page/part index for multi-page works); user-supplied --output layout flattening distinct items to one path; combining page ranges so rows overlap.

Related errors


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