jackwener/OpenCLI · error · CommandExecutionError

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

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.

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)

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.