hashicorp/packer · error
Unable to write
Error message
Unable to write %s: %s
What it means
After appending the new build, the post-processor serializes the manifest with json.MarshalIndent and writes it to output_path via os.WriteFile. If the write fails, PostProcess returns this error while still keeping the source artifact. The build itself succeeded, but the manifest could not be persisted.
Solutions
- Create the parent directory of output_path (mkdir -p) before running packer.
- Fix permissions on the existing manifest/its directory (chown/chmod) or run packer as the same user that owns it.
- Verify output_path is a file path on a writable, non-full filesystem.
- Simplify output_path to a plain writable path like `packer-manifest.json` to rule out interpolation issues.
Example fix
// before output_path = "artifacts/manifests/packer-manifest.json" // dir doesn't exist // after mkdir -p artifacts/manifests # or output_path = "packer-manifest.json"
Defensive patterns
Strategy: validation
Validate before calling
# preflight: manifest's parent dir exists, is writable, and is a file path MP="artifacts/manifests/packer-manifest.json" mkdir -p "$(dirname "$MP")" [ ! -d "$MP" ] && [ -w "$(dirname "$MP")" ] || exit 1
Prevention
- mkdir -p the parent of output_path before building.
- Verify output_path is not a directory.
- Ensure the filesystem is writable and has free space.
- Keep manifest ownership consistent with the packer user.
When it happens
Trigger: os.WriteFile(p.config.OutputPath, out, 0664) fails — output directory missing, permission denied, output_path is a directory, read-only filesystem, or disk full.
Common situations: output_path's parent directory does not exist (packer does not create nested dirs for the manifest); manifest locked/owned by another user from a prior sudo run; writing to a read-only CI volume or a path whose directory was cleaned mid-run.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- Unable to open for reading
- Error interpolating custom data
- Unable to create archive
- Unable to create dir for archive
- unable to create dir
AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05).
Data as JSON: /api/errors/594c04b56d5836f3.
Report an issue: GitHub.
Appendix: source
Thrown at post-processor/manifest/post-processor.go:170
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)
manifestFile.LastRunUUID = os.Getenv("PACKER_RUN_UUID")
// Write JSON to disk
if out, err := json.MarshalIndent(manifestFile, "", " "); err == nil {
if err = os.WriteFile(p.config.OutputPath, out, 0664); err != nil {
return source, true, true, fmt.Errorf("Unable to write %s: %s", p.config.OutputPath, err)
}
} else {
return source, true, true, fmt.Errorf("Unable to marshal JSON %s", err)
}
// The manifest should never delete the artifacts it is set to record, so it
// forcibly sets "keep" to true.
return source, true, true, nil
}
func createInterpolatedCustomData(config *Config, customData string) (string, error) {
interpolatedCmd, err := interpolate.Render(customData, &config.ctx)
if err != nil {
return "", fmt.Errorf("Error interpolating custom data: %s", err)
}
return interpolatedCmd, nil
}
View on GitHub (pinned to eb36e3c3e4)