{"record":{"id":"e165570d0eae4eff","repo":"jackwener/OpenCLI","slug":"failed-to-inspect-pixiv-download-target-target","errorCode":null,"errorMessage":"Failed to inspect Pixiv download target ${target}: ${error?.message || error}","messagePattern":"Failed to inspect Pixiv download target (.+?): (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/pixiv/novel-download-utils.js","lineNumber":106,"sourceCode":"  let canonicalAncestor;\n  try {\n    canonicalAncestor = fs.realpathSync.native(ancestor);\n  } catch {\n    throw new ArgumentError(`output path is not a safe directory: ${ancestor}`);\n  }\n  if (!fs.statSync(canonicalAncestor).isDirectory()) {\n    throw new ArgumentError(`output path is not a safe directory: ${ancestor}`);\n  }\n  return path.join(canonicalAncestor, ...missingParts);\n}\n\nexport function pixivPathEntryExists(target) {\n  try {\n    fs.lstatSync(target);\n    return true;\n  } catch (error) {\n    if (error?.code === 'ENOENT') return false;\n    throw new CommandExecutionError(`Failed to inspect Pixiv download target ${target}: ${error?.message || error}`);\n  }\n}\n\nexport async function fetchNovelForDownload(page, id) {\n  const body = await pixivFetch(page, `/ajax/novel/${id}`, {\n    notFoundMsg: `Novel not found: ${id}`,\n  });\n  return requireNovelDownloadBody(body, id);\n}\n\nexport function formatNovelContent(body, format) {\n  const tags = tagsToString(body.tags);\n  const url = `https://www.pixiv.net/novel/show.php?id=${body.id}`;\n  if (format === 'md') {\n    return [\n      `# ${body.title}`,\n      '',\n      `- Author: ${body.userName}`,","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/pixiv/novel-download-utils.js#L88-L124","documentation":"pixivPathEntryExists uses fs.lstatSync to test whether a download destination exists. ENOENT is treated as 'does not exist', but any other lstat failure (EACCES, ELOOP, ENOTDIR, EIO, etc.) is unrecoverable for the safety check, so a CommandExecutionError wrapping the underlying message is thrown.","triggerScenarios":"prepareIllustPlan, commitIllustPlan, or prepareNovelFile probes a destination path whose parent directory denies permission, contains a symlink loop, has a non-directory component, or the path is on a failing volume.","commonSituations":"Output directory made read-only or owned by another user; destination path contains a dead symlink; output path passes through a file (ENOTDIR); disk/USB/network drive dropped offline mid-session.","solutions":["Fix permissions on the destination's parent directories (chmod/chown for the running user).","Remove symlink loops or dead links in the path (find -type l -xtype l).","Ensure every component of the output path except the final entry is a directory.","Reconnect/remount the storage volume and retry; if transient, wrap the call in retry with backoff."],"exampleFix":"// before\n-o /mnt/usb/novels   // usb unmounted -> EIO on lstat\n// after (shell)\n$ mount /mnt/usb && pixiv-novel download 12345 -o /mnt/usb/novels","handlingStrategy":"retry","validationCode":"import fs from 'node:fs';\nfunction canProbe(p) {\n  try { fs.lstatSync(p); return true; }\n  catch (e) { return e.code === 'ENOENT'; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await downloadNovel(id, { output });\n} catch (e) {\n  if (e.message.startsWith('Failed to inspect Pixiv download target')) {\n    console.error(`Fix filesystem access for output path: ${e.message}`);\n    // remount/chmod, then retry once\n  } else throw e;\n}","preventionTips":["Keep output directories owned by and writable to the running user.","Avoid symlink loops in destination paths.","Verify network/USB volumes are mounted before batch runs.","Keep every path component above the destination a real directory."],"tags":["filesystem","permissions","lstat","path"],"backgroundTag":"path-not-accessible","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}