jackwener/OpenCLI · error · ArgumentError

output must be a directory path

Error message

output must be a directory path

What it means

normalizePixivOutputRoot validates the --output option before any download is written. It throws ArgumentError('output must be a directory path') when the output value is supplied but is not a string (e.g. a number, boolean, or object was passed). The library only accepts a directory path string for output.

Source

Thrown at clis/pixiv/novel-download-utils.js:60

    wordCount,
    bookmarkCount,
  };
}

export function normalizeNovelFileFormat(value) {
  if (value !== undefined && typeof value !== 'string') {
    throw new ArgumentError('Novel download format must be txt or md');
  }
  const format = (value ?? 'txt').toLowerCase();
  if (format !== 'txt' && format !== 'md') {
    throw new ArgumentError(`Unsupported novel download format: ${format}. Supported formats: txt, md.`);
  }
  return format;
}

export function normalizePixivOutputRoot(value, fallback) {
  if (value !== undefined && typeof value !== 'string') {
    throw new ArgumentError('output must be a directory path');
  }
  const raw = value ?? fallback;
  if (!raw || raw.includes('\0')) {
    throw new ArgumentError('output must be a non-empty directory path');
  }
  const resolved = path.resolve(raw);
  let ancestor = resolved;
  const missingParts = [];
  let ancestorStat;
  while (!ancestorStat) {
    try {
      ancestorStat = fs.lstatSync(ancestor);
    } catch (error) {
      if (error?.code !== 'ENOENT') {
        throw new ArgumentError(`output path is not a safe directory: ${ancestor}`);
      }
      const parent = path.dirname(ancestor);
      if (parent === ancestor) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Pass a string directory path, e.g. output: './pixiv-downloads/novels'.
  2. Quote numeric-looking paths in config files (output: "2024") so YAML/JSON does not coerce them to numbers.
  3. Coerce with String(value) before calling, or omit the argument entirely to use the built-in fallback ('./pixiv-downloads/novels').

Example fix

// before
await downloadNovel(id, { output: 2024 });
// after
await downloadNovel(id, { output: String(config.output ?? './pixiv-downloads/novels') });
Defensive patterns

Strategy: type-guard

Validate before calling

if (output !== undefined && typeof output !== 'string') {
  throw new TypeError('output must be a string directory path');
}

Type guard

function isDirectoryPath(v) {
  return v === undefined || (typeof v === 'string' && v.length > 0);
}

Try / catch

try {
  await downloadNovel(id, { output });
} catch (e) {
  if (e.name === 'ArgumentError' && /output must be a directory path/.test(e.message)) {
    output = String(output);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling normalizePixivOutputRoot (directly or via outputRoot/outputDir/output) with a defined non-string value such as normalizePixivOutputRoot(123), normalizePixivOutputRoot(true), or a parsed config object where a string path was expected.

Common situations: Programmatic use of the CLI API passing a numeric or boolean option; YAML/JSON config that parses --output: 2024 (a number) instead of quoting it; a script variable holding null-coalesced value of wrong type.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


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