{"record":{"id":"0a953187ce17b2fe","repo":"jackwener/OpenCLI","slug":"refusing-to-write-local-pixiv-downloads-pass-ex","errorCode":null,"errorMessage":"Refusing to write local Pixiv downloads: pass --execute","messagePattern":"Refusing to write local Pixiv downloads: pass --execute","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/pixiv/bookmark-download.js","lineNumber":28,"sourceCode":"  commitNovelFile,\n  fetchNovelForDownload,\n  normalizeNovelFileFormat,\n  normalizePixivOutputRoot,\n  pixivPathEntryExists,\n  prepareNovelFile,\n} from './novel-download-utils.js';\n\nconst IMAGE_CONTENT_TYPES = new Map([\n  ['.jpg', 'image/jpeg'],\n  ['.jpeg', 'image/jpeg'],\n  ['.png', 'image/png'],\n  ['.gif', 'image/gif'],\n  ['.webp', 'image/webp'],\n]);\n\nfunction requireExecute(value) {\n  if (value !== true) {\n    throw new ArgumentError('Refusing to write local Pixiv downloads: pass --execute');\n  }\n}\n\nfunction parsePixivImageUrl(value, label) {\n  if (typeof value !== 'string' || !value) {\n    throw new CommandExecutionError(`${label} returned a missing image URL`);\n  }\n  let url;\n  try {\n    url = new URL(value);\n  } catch {\n    throw new CommandExecutionError(`${label} returned a malformed image URL`);\n  }\n  const extension = path.extname(url.pathname).toLowerCase();\n  const contentType = IMAGE_CONTENT_TYPES.get(extension);\n  if (url.protocol !== 'https:' || url.hostname !== 'i.pximg.net' || url.username || url.password || url.port || !contentType) {\n    throw new CommandExecutionError(`${label} returned an untrusted Pixiv image URL`);\n  }","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/pixiv/bookmark-download.js#L10-L46","documentation":"This ArgumentError is thrown by requireExecute in clis/pixiv/bookmark-download.js:26-30. The pixiv bookmark-download command runs in a dry-run mode by default and refuses to write files to disk unless --execute is explicitly passed. This is a safety gate so downloads only touch the local filesystem when the user intends it.","triggerScenarios":"Invoking the pixiv bookmark-download command (directly or via its CLI wrapper) without passing --execute, so requireExecute receives value !== true at clis/pixiv/bookmark-download.js:27.","commonSituations":"Running the command in a script or cron job and forgetting the --execute flag; assuming dry-run output already downloaded the files; an older wrapper or alias that omits the flag; copying an example invocation that only previews the plan.","solutions":["Re-run the command with the --execute flag","Verify your wrapper/script passes --execute through to the underlying command","Check the dry-run output first to confirm the planned downloads, then add --execute"],"exampleFix":"// before\nopencli pixiv bookmark-download\n// after\nopencli pixiv bookmark-download --execute","handlingStrategy":"validation","validationCode":"const args = process.argv.slice(2);\nif (!args.includes('--execute')) {\n  console.error('pixiv bookmark-download is dry-run only; pass --execute to write files.');\n  process.exit(1);\n}","typeGuard":"function willExecute(options) {\n  return options === true || (typeof options === 'object' && options !== null && options.execute === true);\n}","tryCatchPattern":"try {\n  await runBookmarkDownload({ execute: true });\n} catch (e) {\n  if (e instanceof ArgumentError && e.message.includes('--execute')) {\n    console.error('Dry-run refused to write files. Re-run with --execute.');\n  } else {\n    throw e;\n  }\n}","preventionTips":["Always pass --execute when you actually want files written to disk","Treat a run without --execute as a preview/plan only — do not expect downloaded files","Wrap the command in scripts that forward flags explicitly rather than hardcoding an invocation","Check the dry-run plan output first, then re-run with --execute"],"tags":["cli","dry-run","missing-flag","pixiv"],"backgroundTag":"missing-required-flag","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}