jackwener/OpenCLI · error · CommandExecutionError

Pixiv image download did not create a valid file: ${file.fil

Error message

Pixiv image download did not create a valid file: ${file.filename}

What it means

After a successful-looking download and validateImageDownload, commitIllustPlan lstats the staged file and requires it to be a real, non-symlink, non-empty file. This CommandExecutionError is thrown when the staged path is missing, a symlink, a directory, or zero bytes, meaning the downloader reported success but did not actually materialize the image. It is a post-download integrity guard.

Source

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

async function commitIllustPlan(plan, cookies) {
  const parent = path.dirname(plan.finalPath);
  let staging;
  try {
    fs.mkdirSync(parent, { recursive: true });
    staging = fs.mkdtempSync(path.join(parent, `.opencli-${plan.illustId}-`));
    for (const file of plan.files) {
      const destination = path.join(staging, file.filename);
      const result = await httpDownload(file.url, destination, {
        cookies,
        headers: { Referer: 'https://www.pixiv.net/' },
        timeout: 60000,
        includeContentType: true,
      });
      validateImageDownload(result, file);
      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}`);
  }
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the download; transient empty responses are the most common cause
  2. Check free disk space and that the staging directory is writable
  3. Inspect the staging path for a symlink or directory and remove anything unexpected
  4. Verify the download command actually writes to the expected destination path (no cwd/path mismatch)
  5. Update the download tool/driver if zero-byte 'successful' downloads recur

Example fix

// before: trusting validateImageDownload alone
validateImageDownload(result, file);
// after: also fail fast on empty responses before committing
validateImageDownload(result, file);
if (result.size <= 0) throw new CommandExecutionError(`empty body for ${file.filename}`);
Defensive patterns

Strategy: validation

Validate before calling

const stat = fs.lstatSync(destination, { throwIfNoEntry: false });
if (!stat || stat.isSymbolicLink() || !stat.isFile() || stat.size <= 0) {
  throw new Error(`staged file invalid before commit: ${destination}`);
}

Type guard

function isRegularNonEmptyFile(p) {
  try {
    const s = fs.lstatSync(p);
    return s.isFile() && !s.isSymbolicLink() && s.size > 0;
  } catch { return false; }
}

Try / catch

try {
  fs.lstatSync(destination);
} catch (err) {
  if (err.code === 'ENOENT') {
    console.warn(`Download produced no file for ${file.filename}; retrying`);
    return retryDownload(file);
  }
  throw err;
}

Prevention

When it happens

Trigger: fs.lstatSync(destination) on the staging path returns a stat where stat.isSymbolicLink(), !stat.isFile(), or stat.size <= 0, right after validateImageDownload passed for file.filename.

Common situations: The download tool wrote to a different working directory than expected; a symlink-hijacking or disk issue replaced the file; the download command reported success but wrote 0 bytes on a flaky network or rate-limited response; antivirus/cleanup removed the staged file mid-run.

Related errors


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