{"record":{"id":"f1c43f4e794eeeed","repo":"hashicorp/packer","slug":"failed-to-create-destination-directory-for-user-sb","errorCode":null,"errorMessage":"failed to create destination directory for user SBOM: %s","messagePattern":"failed to create destination directory for user SBOM: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"provisioner/hcp-sbom/provisioner.go","lineNumber":448,"sourceCode":"\tif err == nil {\n\t\tif info.IsDir() {\n\t\t\t// If the destination is a directory, create a temporary file inside it\n\t\t\ttmpFile, err := os.CreateTemp(dst, \"packer-user-sbom-*.json\")\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", fmt.Errorf(\"failed to create temporary file in user SBOM directory %s: %s\", dst, err)\n\t\t\t}\n\t\t\tdst = tmpFile.Name()\n\t\t\t_ = tmpFile.Close() // Ignore error on close after getting name\n\t\t}\n\t\treturn dst, nil\n\t}\n\n\toutDir := filepath.Dir(dst)\n\t// In case the destination does not exist, we'll get the dirpath,\n\t// and create it if it doesn't already exist\n\terr = os.MkdirAll(outDir, 0755)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create destination directory for user SBOM: %s\", err)\n\t}\n\n\t// Check if the destination is a directory after the previous step.\n\t//\n\t// This happens if the path specified ends with a `/`, in which case the\n\t// destination is a directory, and we must create a temporary file in\n\t// this destination directory.\n\tdestStat, statErr := os.Stat(dst)\n\tif statErr == nil && destStat.IsDir() {\n\t\ttmpFile, err := os.CreateTemp(outDir, \"packer-user-sbom-*.json\")\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to create temporary file in user SBOM directory %s: %s\", dst, err)\n\t\t}\n\t\tdst = tmpFile.Name()\n\t\t_ = tmpFile.Close() // Ignore error on close after getting name\n\t}\n\n\treturn dst, nil","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/hashicorp/packer/blob/eb36e3c3e48a036f3e8cc94087636ee72e1303c9/provisioner/hcp-sbom/provisioner.go#L430-L466","documentation":"If the configured destination path does not exist, getUserDestination derives its parent directory (filepath.Dir) and attempts to create the missing directory tree with os.MkdirAll(outDir, 0755). Failure (permission denied on an ancestor, read-only filesystem, path component is a file, invalid name) is wrapped and returned as this error, so SBOM writing aborts.","triggerScenarios":"`destination` path does not exist and os.MkdirAll on its parent fails: an ancestor directory is not writable, a path component exists as a regular file (e.g. /out is a file, destination /out/sbom.json), or the filesystem is read-only.","commonSituations":"Destination like `/opt/mytool/sbom.json` where /opt/mytool cannot be created by the current user; destination under a path where an intermediate name is actually a file; running Packer inside a container whose target volume is mounted read-only; typo making the path land under a non-writable root (e.g. `/proot/...`).","solutions":["Create the parent directory manually with correct ownership: `mkdir -p <parent> && chown $(id -u) <parent>`.","Choose a destination under a writable path (project dir, $HOME, output_directory of the builder).","If a path component is a file, remove/rename it so the directory can be created (`ls -l` each component of the path).","If running in a container/CI, mount the output volume rw and confirm with `mount | grep <path>`.","Check `ENOTDIR`/`EACCES` in the wrapped error message to distinguish not-a-directory from permission problems."],"exampleFix":"// before\ndestination = \"/opt/sbom-tool/out/sbom.json\" // /opt/sbom-tool not creatable\n\n// after\ndestination = \"./out/sbom.json\"","handlingStrategy":"validation","validationCode":"// Validate ancestors of the destination before building:\ndst=\"/opt/sbom-tool/out/sbom.json\"\nparent=\"$(dirname \"$dst\")\"\nif [ -e \"$dst\" ] && [ -f \"$dst\" ]; then echo \"$dst exists as dir-parent? check\"; fi\n# ensure no component of $parent is a regular file and the nearest existing ancestor is writable\nwhile [ ! -e \"$parent\" ]; do parent=\"$(dirname \"$parent\")\"; done\n[ -w \"$parent\" ] || { echo \"$parent not writable\"; exit 1; }","typeGuard":null,"tryCatchPattern":"dst, err := p.getUserDestination()\nif err != nil {\n    var perr *fs.PathError\n    if errors.As(err, &perr) && errors.Is(perr.Err, syscall.ENOTDIR) {\n        return fmt.Errorf(\"a component of %s is a file, not a directory\", perr.Path)\n    }\n    return err\n}","preventionTips":["Pre-create the destination parent with mkdir -p and correct ownership","Ensure no intermediate path component is a regular file","Use relative destinations under the working dir or $HOME in CI","Verify container volumes are mounted read-write","Read the wrapped error: EACCES means permissions, ENOTDIR means a file is in the path"],"tags":["filesystem","permissions","provisioner","mkdir"],"backgroundTag":"mkdir-permission-denied","analyzedSha":"eb36e3c3e48a036f3e8cc94087636ee72e1303c9","analyzedAt":"2026-09-05T13:20:43.127Z","contentChangedAt":"2026-09-05T13:20:43.127Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}