hashicorp/packer · error
Unable to open %s for reading: %s
Error message
Unable to open %s for reading: %s
What it means
During PostProcess the manifest post-processor reads the existing packer-manifest.json from output_path to append the new build. If os.ReadFile fails with an error other than NotExist, it returns this error (keeping the source artifact and force flag). A non-notexist failure means the manifest exists but can't be read.
Source
Thrown at post-processor/manifest/post-processor.go:146
// Create a lock file with exclusive access. If this fails we will retry
// after a delay.
lockFilename := p.config.OutputPath + ".lock"
for i := 0; i < 3; i++ {
// The file should not be locked for very long so we'll keep this short.
time.Sleep((time.Duration(i) * 200 * time.Millisecond))
_, err = os.OpenFile(lockFilename, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
if err == nil {
break
}
log.Printf("Error locking manifest file for reading and writing. Will sleep and retry. %s", err)
}
defer os.Remove(lockFilename)
// Read the current manifest file from disk
var contents []byte
if contents, err = os.ReadFile(p.config.OutputPath); err != nil && !os.IsNotExist(err) {
return source, true, true, fmt.Errorf("Unable to open %s for reading: %s", p.config.OutputPath, err)
}
// Parse the manifest file JSON, if we have one
manifestFile := &ManifestFile{}
if len(contents) > 0 {
if err = json.Unmarshal(contents, manifestFile); err != nil {
return source, true, true, fmt.Errorf("Unable to parse content from %s: %s", p.config.OutputPath, err)
}
}
// If -force is set and we are not on same run, truncate the file. Otherwise
// we will continue to add new builds to the existing manifest file.
if p.config.PackerForce && os.Getenv("PACKER_RUN_UUID") != manifestFile.LastRunUUID {
manifestFile = &ManifestFile{}
}
// Add the current artifact to the manifest file
manifestFile.Builds = append(manifestFile.Builds, *artifact)View on GitHub (pinned to eb36e3c3e4)
Solutions
- Check permissions on the existing manifest file: chown/chmod it so the packer user can read it (or delete it if stale).
- Verify output_path points to a file, not a directory.
- Confirm the output directory is on a mounted, writable filesystem.
- If the manifest is stale/corrupt, remove it and re-run — the post-processor recreates it.
Example fix
// before: manifest owned by root, packer runs as user sudo chown $(whoami) packer-manifest.json // or remove stale manifest rm packer-manifest.json
Defensive patterns
Strategy: validation
Validate before calling
# preflight manifest readability
MP=packer-manifest.json
if [ -e "$MP" ]; then [ -f "$MP" ] && [ -r "$MP" ] || { echo "manifest unreadable: $MP"; exit 1; }; fi Prevention
- Run packer consistently as one user; avoid sudo mixing on manifest files.
- Ensure output_path is a file, not a directory.
- Delete stale manifests from previous (root) runs.
- Check mounts are up before CI runs.
When it happens
Trigger: os.ReadFile(p.config.OutputPath) fails and the error is not os.IsNotExist — e.g. permission denied on the manifest file, output_path resolves to a directory, or an I/O error on the volume.
Common situations: packer-manifest.json left owned by root from a previous sudo run; output_path set to a directory instead of a file; read-only or unmounted output filesystem; NFS permission issues.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- Unable to write %s: %s
- unable to create dir: %s
- unable to create file %s: %s
- Unable to create dir for archive %s: %s
- Unable to create archive %s: %s
AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05).
Data as JSON: /api/errors/e70b45dd70603ac4.
Report an issue: GitHub.