apache/beam · error

expected header as first message: %v

Error message

expected header as first message: %v

What it means

PutArtifact requires the first received message to be a header with embedded ArtifactMetadata. If the metadata is nil, the client sent a data chunk (or malformed message) instead of the header, so the server rejects the stream with this error.

Source

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

			return nil, errors.Errorf("staged artifact for %v has invalid SHA256: %v, want %v", a.Name, info.hash, a.Sha256)
		}

		loc = append(loc, &jobpb.ProxyManifest_Location{Name: a.Name, Uri: info.object})
	}
	return loc, nil
}

// PutArtifact stores the given artifact in GCS.
func (s *StagingServer) PutArtifact(ps jobpb.LegacyArtifactStagingService_PutArtifactServer) error {
	// Read header

	header, err := ps.Recv()
	if err != nil {
		return errors.Wrap(err, "failed to receive header")
	}
	md := header.GetMetadata().GetMetadata()
	if md == nil {
		return errors.Errorf("expected header as first message: %v", header)
	}
	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 {

View on GitHub (pinned to 12126d8942)

Solutions

  1. Ensure the client sends a WriteToStreamRequest containing ArtifactMetadata as the very first message
  2. Update the legacy staging client to match the server's expected proto version
  3. Log the received header server-side to identify what the client actually sent

Example fix

// before: chunk sent first
stream.Send(&jobpb.WriteToStreamRequest{Data: chunk})

// after: header first
stream.Send(&jobpb.WriteToStreamRequest{Metadata: &jobpb.ArtifactMetadata{Name: name, Sha256: sum}})
stream.Send(&jobpb.WriteToStreamRequest{Data: chunk})
Defensive patterns

Strategy: validation

Validate before calling

// client side: verify first message is the header
if req.GetMetadata().GetMetadata() == nil {
    return errors.New("first stream message must be the artifact header")
}

Try / catch

if err := stream.PutArtifact(ctx); err != nil && strings.Contains(err.Error(), "expected header") {
    // fix client to send header first, then retry
}

Prevention

When it happens

Trigger: Client writes artifact chunks before the header message; client sends a message whose oneof is not metadata; protocol bug in a custom staging client.

Common situations: Hand-rolled or version-mismatched staging clients that omit or misorder the header; mixing up legacy and current artifact staging APIs.

Related errors


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