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
- Ensure the client sends a WriteToStreamRequest containing ArtifactMetadata as the very first message
- Update the legacy staging client to match the server's expected proto version
- 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
- Always send ArtifactMetadata as the first WriteToStreamRequest
- Keep client and server proto versions aligned
- Write an integration test asserting the header-first protocol
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
- failed to receive header
- failed to send chunks for %v; close error: %v
- failed to close stream for %v; response: %v
- chunk send failed
- Logging stream terminated unexpectedly with success before i
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/9b7b3ea237af1b72.
Report an issue: GitHub.