{"record":{"id":"4e3a53486d33f4f1","repo":"jdx/mise","slug":"copy-image-path-image-path-ends-with-but-4e3a53","errorCode":null,"errorMessage":"copy image path {image_path:?} ends with `/` but {host_path.display} is not a directory; specify the exact destination file name","messagePattern":"copy image path (.+?) ends with `/` but (.+?) is not a directory; specify the exact destination file name","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/oci/layer.rs","lineNumber":174,"sourceCode":"/// Directory contents are placed under `image_path`; a file or symlink is\n/// placed at `image_path` itself.\npub(crate) fn build_layer_from_path(\n    host_path: &Path,\n    image_path: &str,\n    owner: LayerOwner,\n) -> Result<LayerBlob> {\n    let metadata = std::fs::symlink_metadata(host_path)\n        .wrap_err_with(|| format!(\"reading metadata for {}\", host_path.display()))?;\n    let target = image_path.trim_matches('/');\n\n    if metadata.is_dir() {\n        return build_layer_from_dir(host_path, target, owner);\n    }\n    if target.is_empty() {\n        eyre::bail!(\"cannot copy a file or symlink to the image root\");\n    }\n    if image_path.ends_with('/') {\n        eyre::bail!(\n            \"copy image path {image_path:?} ends with `/` but {} is not a directory; \\\n             specify the exact destination file name\",\n            host_path.display()\n        );\n    }\n\n    let kind = if metadata.file_type().is_symlink() {\n        EntryKind::Symlink(\n            std::fs::read_link(host_path)\n                .wrap_err_with(|| format!(\"reading symlink {}\", host_path.display()))?,\n        )\n    } else if metadata.is_file() {\n        EntryKind::File\n    } else {\n        eyre::bail!(\"unsupported host path type: {}\", host_path.display());\n    };\n    let mode = match &kind {\n        EntryKind::Symlink(_) => 0o777,","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/oci/layer.rs#L156-L192","documentation":"In build_layer_from_path, a trailing '/' on the image path signals directory-copy semantics, so when the host path is a plain file (or symlink) the destination ending in '/' is contradictory. This error tells the caller to specify the exact destination file name instead of a directory-style path.","triggerScenarios":"Calling build_layer_from_path with a non-directory host_path and image_path with a trailing slash, e.g. { src = \"app.conf\", dest = \"/etc/app/\" } — the file branch is taken, image_path.ends_with('/') is true, and the bail fires.","commonSituations":"Copy-pasting directory-style destinations from another tool (docker COPY semantics); templated dest built by joining a dir prefix plus '/' for a file entry; user expecting the file to be renamed automatically into the directory.","solutions":["Remove the trailing slash and give the full destination file path: dest = \"/etc/app/app.conf\".","If directory semantics are wanted, point src at a directory so build_layer_from_dir runs instead.","Adjust templates/generators that append '/' to every destination regardless of whether src is a file."],"exampleFix":"// before\n{ src = \"./app.conf\", dest = \"/etc/app/\" }\n// after\n{ src = \"./app.conf\", dest = \"/etc/app/app.conf\" }","handlingStrategy":"validation","validationCode":"function assertDestMatchesSrcKind(src, dest) {\n  const isDir = fs.statSync(src).isDirectory();\n  if (!isDir && dest.endsWith(\"/\")) {\n    throw new Error(`file src needs exact file dest, not dir-style: ${dest}`);\n  }\n}","typeGuard":null,"tryCatchPattern":"match build_layer_from_path(&host, image_path, owner) {\n    Err(e) if e.to_string().contains(\"ends with `/`\") => {\n        eprintln!(\"drop trailing slash and name the file: {image_path} -> {image_path}/{file_name}\");\n    }\n    r => r,\n}","preventionTips":["Strip trailing slashes from generated destinations for file entries.","Only add trailing slash when src is a directory.","Document copy semantics (docker COPY-style) in your config schema."],"tags":["oci","layer","path"],"backgroundTag":"invalid-argument-format","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}