jackwener/OpenCLI · error · CommandExecutionError

Pixiv illustration download failed

Error message

Pixiv illustration ${plan.illustId} download failed: ${error?.message || error}

What it means

commitIllustPlan wraps its whole staging/commit work in try/catch; any failure that is not already a CommandExecutionError (rename failures, permission errors, network errors from the downloader, fs errors) is re-thrown as this generic per-illustration error carrying the illustId. Already-typed CommandExecutionErrors pass through unchanged. The staging directory and any created directories are cleaned up before throwing.

Solutions

  1. Read the inner error.message in the message to identify the root cause (network vs filesystem vs auth)
  2. Re-run the download for that illustId after confirming the network and Pixiv session are healthy
  3. Check permissions and free space on the output/staging directories
  4. Re-login/refresh Pixiv cookies if the inner message indicates an authentication or page fetch problem
  5. Wrap known failure points in typed CommandExecutionErrors if you need finer-grained handling

Example fix

// before: opaque error, cause lost
throw new CommandExecutionError(`Pixiv illustration ${plan.illustId} download failed: ${error?.message || error}`);
// after: preserve the original error for debugging
const wrapped = new CommandExecutionError(`Pixiv illustration ${plan.illustId} download failed: ${error?.message || error}`);
wrapped.cause = error;
throw wrapped;
Defensive patterns

Strategy: try-catch

Validate before calling

if (!plan || !plan.illustId || !Array.isArray(plan.files)) {
  throw new Error('invalid illust plan before commit');
}

Type guard

function isCommandExecutionError(e) {
  return e instanceof Error && e.constructor.name === 'CommandExecutionError';
}

Try / catch

try {
  await commitIllustPlan(plan, cookies);
} catch (err) {
  console.error(`Illustration ${plan.illustId} failed: ${err.message}`);
  failedIds.push(plan.illustId); // continue with the rest, retry later
}

Prevention

When it happens

Trigger: Any exception inside commitIllustPlan other than a CommandExecutionError — e.g. fetch/failure from the Pixiv download call, fs.renameSync/lstatSync/rmSync throwing (ENOENT, EACCES, EBUSY), or any third-party library error during file staging.

Common situations: Network interruption mid-image download; the pixiv page/session expired so the download call rejects; output disk full or read-only; filesystem error moving the staged file into place; unexpected null/undefined dereferenced inside the commit path.

Related errors


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

Appendix: source

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

      const stat = fs.lstatSync(destination);
      if (stat.isSymbolicLink() || !stat.isFile() || stat.size <= 0) {
        throw new CommandExecutionError(`Pixiv image download did not create a valid file: ${file.filename}`);
      }
    }
    if (pixivPathEntryExists(plan.finalPath)) {
      throw new CommandExecutionError(`Refusing to overwrite existing Pixiv download: ${plan.finalPath}`);
    }
    fs.renameSync(staging, plan.finalPath);
    return plan.finalPath;
  } catch (error) {
    if (staging) {
      try { fs.rmSync(staging, { recursive: true, force: true }); } catch {}
    }
    for (const directory of plan.createdDirs) {
      try { fs.rmdirSync(directory); } catch {}
    }
    if (error instanceof CommandExecutionError) throw error;
    throw new CommandExecutionError(`Pixiv illustration ${plan.illustId} download failed: ${error?.message || error}`);
  }
}

function cleanupPlan(plan) {
  if (plan.kind === 'novel') {
    cleanupNovelFile(plan);
    return;
  }
  try { fs.rmSync(plan.finalPath, { recursive: true, force: true }); } catch {}
  for (const directory of plan.createdDirs) {
    try { fs.rmdirSync(directory); } catch {}
  }
}

cli({
  site: 'pixiv',
  name: 'bookmark-download',
  access: 'read',

View on GitHub (pinned to 49907e53dc)