apache/beam · error

invalid SHA256 for artifact

Error message

invalid SHA256 for artifact %v: %v want %v

What it means

After writing the object, PutArtifact computes the streamed content's SHA256 and compares it to the SHA256 declared in the artifact metadata. If the client declared a non-empty Sha256 that doesn't match the uploaded bytes, staging is rejected.

Solutions

  1. Fix the client to compute SHA256 over the exact bytes it streams
  2. Re-run staging; the client should hash and stream the same file in one pass
  3. Clear the md.Sha256 field if the client cannot compute a hash (server then trusts its own hash)

Example fix

// before: hash of stale file
sum := sha256File("old.jar")
stream(sha256File("new.jar")) // content differs

// after: hash the streamed content
sum := sha256File("new.jar")
stream(sha256File("new.jar"))
Defensive patterns

Strategy: validation

Validate before calling

sum := sha256.Sum256(bytes)
if md.Sha256 != "" && hex.EncodeToString(sum[:]) != md.Sha256 {
    return errors.New("local hash mismatch before upload")
}

Try / catch

if err := stream.PutArtifact(ctx); err != nil && strings.Contains(err.Error(), "invalid SHA256 for artifact") {
    // recompute hash client-side and restage
}

Prevention

When it happens

Trigger: Client sends md.Sha256 that doesn't match the actual bytes streamed in the data chunks — truncated, reordered, or modified upload content.

Common situations: Client computing hash over a different file version than the one streamed; corrupted or interrupted stream losing chunks; client bug hashing before compression/encoding.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/fd95916b6c2d8df0. Report an issue: GitHub.

Appendix: source

Thrown at sdks/go/pkg/beam/artifact/gcsproxy/staging.go:149

	}
	object := path.Join(s.root, md.Name)

	// Stream content to GCS. We don't have to worry about partial
	// or abandoned writes, because object writes are atomic.

	ctx := ps.Context()
	cl, err := gcsx.NewClient(ctx, storage.ScopeReadWrite)
	if err != nil {
		return errors.Wrap(err, "failed to create GCS client")
	}

	r := &reader{sha256W: sha256.New(), stream: ps}
	if err := gcsx.WriteObject(ctx, cl, s.bucket, object, r); err != nil {
		return errors.Wrapf(err, "failed to stage artifact %v", md.Name)
	}
	hash := r.SHA256()
	if md.Sha256 != "" && md.Sha256 != hash {
		return errors.Errorf("invalid SHA256 for artifact %v: %v want %v", md.Name, hash, md.Sha256)
	}

	s.mu.Lock()
	s.blobs[md.Name] = staged{object: gcsx.MakeObject(s.bucket, object), hash: hash}
	s.mu.Unlock()

	return ps.SendAndClose(&jobpb.PutArtifactResponse{})
}

// reader is an adapter between the artifact stream and the GCS stream reader.
// It also computes the SHA256 of the content.
type reader struct {
	sha256W hash.Hash
	buf     []byte
	stream  jobpb.LegacyArtifactStagingService_PutArtifactServer
}

func (r *reader) Read(buf []byte) (int, error) {

View on GitHub (pinned to 12126d8942)