hashicorp/packer · error

hash %q: %w

Error message

hash %q: %w

What it means

While deriving subjects, each file listed by artifact.Files() is hashed with sha256File, which opens the file and copies it into a SHA-256 hasher. If opening or reading a file fails, deriveSubjects wraps the underlying error as `hash %q: %w` naming the file. This means an artifact advertised a file that is missing, unreadable, or failed mid-read.

Source

Thrown at internal/provenance/subject.go:45

	return deriveSubjects(artifact)
}

func DeriveIdentityRecord(artifact packersdk.Artifact) (map[string]interface{}, error) {
	return deriveIdentityRecord(artifact)
}

func deriveSubjects(artifact packersdk.Artifact) ([]Subject, error) {
	if artifact == nil {
		return nil, fmt.Errorf("artifact is nil")
	}

	files := artifact.Files()
	if len(files) > 0 {
		subjects := make([]Subject, 0, len(files))
		for _, file := range files {
			digest, err := sha256File(file)
			if err != nil {
				return nil, fmt.Errorf("hash %q: %w", file, err)
			}

			subjects = append(subjects, Subject{
				Name: filepath.Base(file),
				Digest: DigestSet{
					"sha256": digest,
				},
			})
		}

		return subjects, nil
	}

	identity, err := deriveIdentityRecord(artifact)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to eb36e3c3e4)

Solutions

  1. Inspect the wrapped cause (%w) to distinguish os.Open 'no such file or directory' vs 'permission denied' vs read error
  2. Verify the file path exists and is readable: ls -l / stat the path from the error message
  3. Re-run the build so the artifact files are regenerated consistently
  4. Check the builder plugin's Files() implementation for stale or incorrect paths
  5. Run with sufficient privileges if the artifact lives in a protected location

Example fix

// before
subjects, err := provenance.DeriveSubjects(artifact)
if err != nil { return err }
// after
subjects, err := provenance.DeriveSubjects(artifact)
if err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) && os.IsNotExist(pe) {
        log.Warnf("skipping provenance, artifact file missing: %v", err)
        return nil
    }
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

for _, f := range artifact.Files() {
    if _, err := os.Stat(f); err != nil {
        return fmt.Errorf("artifact file unavailable before hashing: %w", err)
    }
}

Try / catch

subjects, err := provenance.DeriveSubjects(artifact)
if err != nil {
    var perr *fs.PathError
    if errors.As(err, &perr) { return fmt.Errorf("cannot hash artifact file %s: %w", perr.Path, perr.Err) }
    return err
}

Prevention

When it happens

Trigger: artifact.Files() returns a path that does not exist on disk; the file exists but lacks read permission; the file is a dangling symlink; an I/O error occurs while reading; file was deleted between Files() and hashing.

Common situations: Docker/docker export artifacts referencing removed temp files; artifacts built on a different host or container layer; running packer as a non-root user without access to builder output; NFS/overlay filesystem issues during long builds.

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/97d6ef571fdd62f1. Report an issue: GitHub.