{"record":{"id":"b4fc9c3d4b1be7ac","repo":"jackwener/OpenCLI","slug":"refusing-to-write-a-local-pixiv-novel-pass-exec","errorCode":null,"errorMessage":"Refusing to write a local Pixiv novel: pass --execute","messagePattern":"Refusing to write a local Pixiv novel: pass --execute","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"info","filePath":"clis/pixiv/novel-download.js","lineNumber":21,"sourceCode":"import { fetchNovelForDownload, normalizeNovelFileFormat, normalizePixivOutputRoot, writeNovelFile } from './novel-download-utils.js';\n\ncli({\n  site: 'pixiv',\n  name: 'novel-download',\n  access: 'read',\n  description: 'Download Pixiv novel text as txt or markdown',\n  domain: 'www.pixiv.net',\n  strategy: Strategy.COOKIE,\n  args: [\n    { name: 'novel-id', positional: true, required: true, help: 'Novel ID' },\n    { name: 'output', default: './pixiv-downloads/novels', help: 'Output directory' },\n    { name: 'file-format', default: 'txt', help: 'Output file format: txt or md' },\n    { name: 'execute', type: 'boolean', default: false, help: 'Actually write the local novel file' },\n  ],\n  columns: ['novel_id', 'title', 'format', 'status', 'path'],\n  func: async (page, kwargs) => {\n    if (kwargs.execute !== true) {\n      throw new ArgumentError('Refusing to write a local Pixiv novel: pass --execute');\n    }\n    const id = String(kwargs['novel-id'] ?? '');\n    if (!/^\\d+$/.test(id)) {\n      throw new ArgumentError(`Invalid novel ID: ${id}`, 'Example: opencli pixiv novel-download 10588915 --file-format txt');\n    }\n    const format = normalizeNovelFileFormat(kwargs['file-format'] ?? kwargs.format);\n    const output = normalizePixivOutputRoot(kwargs.output, './pixiv-downloads/novels');\n    const body = await fetchNovelForDownload(page, id);\n    const destPath = writeNovelFile(body, output, format);\n    return [{ novel_id: body.id, title: body.title, format, status: 'success', path: destPath }];\n  },\n});\n","sourceCodeStart":3,"sourceCodeEnd":34,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/pixiv/novel-download.js#L3-L34","documentation":"The pixiv novel-download command writes a local file on disk, which is a destructive/side-effectful action. To prevent accidental writes (e.g. when previewing results or scripting), the command requires an explicit --execute flag. If kwargs.execute is not exactly true, the command throws ArgumentError and writes nothing.","triggerScenarios":"Running `opencli pixiv novel-download <id> ...` without the --execute flag, or passing --execute with a truthy non-boolean value that doesn't strictly equal true (the check is `kwargs.execute !== true`).","commonSituations":"Developers running the command for the first time expecting it to download by default; scripts/CI pipelines that omit --execute; copy-pasted commands from docs that show the dry-run form.","solutions":["Re-run the command with the --execute flag: `opencli pixiv novel-download 10588915 --execute --file-format txt`.","Ensure the flag is parsed as a boolean true — the guard is strict (`!== true`), so quoted or string values like --execute=false or \"true\" will still throw.","If intentionally doing a dry run, this error is expected; treat it as the safety gate and add --execute only when ready to write."],"exampleFix":"// before\nopencli pixiv novel-download 10588915 --file-format txt\n// after\nopencli pixiv novel-download 10588915 --file-format txt --execute","handlingStrategy":"validation","validationCode":"// validate args before invoking the command\nfunction canRunNovelDownload(args) {\n  return args.execute === true && /^\\d+$/.test(String(args['novel-id'] ?? ''));\n}\nif (!canRunNovelDownload(args)) {\n  console.error('Pass --execute and a numeric --novel-id, e.g. opencli pixiv novel-download 10588915 --execute --file-format txt');\n  process.exit(1);\n}","typeGuard":"function hasExecuteFlag(args) {\n  return typeof args === 'object' && args !== null && args.execute === true;\n}","tryCatchPattern":null,"preventionTips":["Always include --execute when the intent is to write the file; omit it only for dry runs.","Wrap write commands in scripts that echo the target path before executing, so dry-run vs write intent is explicit.","Note the strict `=== true` comparison — never pass --execute as a string in programmatic calls."],"tags":["cli","argument-error","safety-guard","dry-run"],"backgroundTag":"missing-required-flag","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}