{"record":{"id":"b782325724f3d102","repo":"BigPizzaV3/CodexPlusPlus","slug":"dream-skin-image-is-not-a-file","errorCode":null,"errorMessage":"Dream Skin image is not a file","messagePattern":"Dream Skin image is not a file","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/dream_skin.rs","lineNumber":53,"sourceCode":"    Ok(destination)\n}\n\npub(crate) fn prepare_dream_skin_image_for_directory(\n    source: &Path,\n    destination_dir: &Path,\n    destination_stem: &str,\n) -> anyhow::Result<PathBuf> {\n    if destination_stem.is_empty()\n        || !destination_stem\n            .bytes()\n            .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_'))\n    {\n        bail!(\"invalid Dream Skin destination name\");\n    }\n    let metadata = std::fs::symlink_metadata(source)\n        .with_context(|| format!(\"failed to read image metadata {}\", source.display()))?;\n    if !metadata.file_type().is_file() || metadata.file_type().is_symlink() {\n        bail!(\"Dream Skin image is not a file\");\n    }\n    if metadata.len() == 0 {\n        bail!(\"Dream Skin image is empty\");\n    }\n    if metadata.len() > DREAM_SKIN_SOURCE_LIMIT {\n        bail!(\"Dream Skin source image exceeds 50 MiB\");\n    }\n\n    let extension = supported_image_extension(source)?;\n    std::fs::create_dir_all(destination_dir).with_context(|| {\n        format!(\n            \"failed to create Dream Skin theme directory {}\",\n            destination_dir.display()\n        )\n    })?;\n\n    let (prepared_path, prepared_extension) = prepare_image(source, destination_dir, &extension)?;\n    let prepared_metadata = std::fs::metadata(&prepared_path).with_context(|| {","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/f2074595a281bc057525c748175c8eb9805b0673/crates/codex-plus-core/src/dream_skin.rs#L35-L71","documentation":"The source is checked with std::fs::symlink_metadata, which does not follow links: the entry must be a regular file and must not itself be a symlink. Directories, FIFOs, device nodes, and symlinks-to-images are all rejected, so the import always reads a real file.","triggerScenarios":"Passing a directory, a Finder alias or an 'ln -s' symlink, a mounted device path, or a stale drag-and-drop path that now resolves to a folder.","commonSituations":"macOS Finder aliases; users symlinking a Pictures folder into the picker; the target being moved or deleted between selection and import.","solutions":["Resolve the selection with std::fs::canonicalize and pass the resolved regular-file path","Filter file-picker results to regular files before enabling import","Prompt for a re-pick when the path no longer resolves to a file"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn is_regular_non_symlink(path: &std::path::Path) -> bool {\n    std::fs::symlink_metadata(path)\n        .map(|m| m.file_type().is_file() && !m.file_type().is_symlink())\n        .unwrap_or(false)\n}","typeGuard":"fn is_regular_non_symlink(path: &std::path::Path) -> bool {\n    std::fs::symlink_metadata(path)\n        .map(|m| m.file_type().is_file() && !m.file_type().is_symlink())\n        .unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Canonicalize drag-and-drop paths before import","Reject symlinks explicitly - the library checks with symlink_metadata and will not follow them","Re-stat immediately before import if the user can modify the file tree in between"],"tags":["dream-skin","filesystem","symlink","rust"],"backgroundTag":"invalid-file-type","analyzedSha":"f2074595a281bc057525c748175c8eb9805b0673","analyzedAt":"2026-08-23T12:52:24.489Z","contentChangedAt":"2026-08-23T12:52:24.489Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}