hashicorp/packer · error
failed to create destination directory for user SBOM: %s
Error message
failed to create destination directory for user SBOM: %s
What it means
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.
Source
Thrown at provisioner/hcp-sbom/provisioner.go:448
if err == nil {
if info.IsDir() {
// If the destination is a directory, create a temporary file inside it
tmpFile, err := os.CreateTemp(dst, "packer-user-sbom-*.json")
if err != nil {
return "", fmt.Errorf("failed to create temporary file in user SBOM directory %s: %s", dst, err)
}
dst = tmpFile.Name()
_ = tmpFile.Close() // Ignore error on close after getting name
}
return dst, nil
}
outDir := filepath.Dir(dst)
// In case the destination does not exist, we'll get the dirpath,
// and create it if it doesn't already exist
err = os.MkdirAll(outDir, 0755)
if err != nil {
return "", fmt.Errorf("failed to create destination directory for user SBOM: %s", err)
}
// Check if the destination is a directory after the previous step.
//
// This happens if the path specified ends with a `/`, in which case the
// destination is a directory, and we must create a temporary file in
// this destination directory.
destStat, statErr := os.Stat(dst)
if statErr == nil && destStat.IsDir() {
tmpFile, err := os.CreateTemp(outDir, "packer-user-sbom-*.json")
if err != nil {
return "", fmt.Errorf("failed to create temporary file in user SBOM directory %s: %s", dst, err)
}
dst = tmpFile.Name()
_ = tmpFile.Close() // Ignore error on close after getting name
}
return dst, nilView on GitHub (pinned to eb36e3c3e4)
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.
Example fix
// before destination = "/opt/sbom-tool/out/sbom.json" // /opt/sbom-tool not creatable // after destination = "./out/sbom.json"
Defensive patterns
Strategy: validation
Validate before calling
// Validate ancestors of the destination before building:
dst="/opt/sbom-tool/out/sbom.json"
parent="$(dirname "$dst")"
if [ -e "$dst" ] && [ -f "$dst" ]; then echo "$dst exists as dir-parent? check"; fi
# ensure no component of $parent is a regular file and the nearest existing ancestor is writable
while [ ! -e "$parent" ]; do parent="$(dirname "$parent")"; done
[ -w "$parent" ] || { echo "$parent not writable"; exit 1; } Try / catch
dst, err := p.getUserDestination()
if err != nil {
var perr *fs.PathError
if errors.As(err, &perr) && errors.Is(perr.Err, syscall.ENOTDIR) {
return fmt.Errorf("a component of %s is a file, not a directory", perr.Path)
}
return err
} Prevention
- 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
When it happens
Trigger: `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.
Common situations: 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/...`).
Understand the failure class
Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.
Related errors
- could not create plugin folder %q: %w
- failed to create temporary file in user SBOM directory %s: %
- could not create final plugin binary file: %w
- failed to get current working directory for Packer SBOM: %s
- unable to create dir: %s
AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05).
Data as JSON: /api/errors/f1c43f4e794eeeed.
Report an issue: GitHub.