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

  1. Check permissions on the existing manifest file: chown/chmod it so the packer user can read it (or delete it if stale).
  2. Verify output_path points to a file, not a directory.
  3. Confirm the output directory is on a mounted, writable filesystem.
  4. 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

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


AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05). Data as JSON: /api/errors/e70b45dd70603ac4. Report an issue: GitHub.