{"record":{"id":"93496b994e1db101","repo":"apache/beam","slug":"staged-artifact-for-v-has-invalid-sha256-v-want-v","errorCode":null,"errorMessage":"staged artifact for %v has invalid SHA256: %v, want %v","messagePattern":"staged artifact for (.+?) has invalid SHA256: (.+?), want (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/artifact/gcsproxy/staging.go","lineNumber":112,"sourceCode":"\t// jobs. Such a server would also use the ID sent with each request.\n\n\treturn &jobpb.CommitManifestResponse{RetrievalToken: gcsx.MakeObject(s.bucket, s.manifest)}, nil\n}\n\n// matchLocations ensures that all artifacts have been staged and have valid\n// content. It is fine for staged artifacts to not appear in the manifest.\nfunc matchLocations(artifacts []*jobpb.ArtifactMetadata, blobs map[string]staged) ([]*jobpb.ProxyManifest_Location, error) {\n\tvar loc []*jobpb.ProxyManifest_Location\n\tfor _, a := range artifacts {\n\t\tinfo, ok := blobs[a.Name]\n\t\tif !ok {\n\t\t\treturn nil, errors.Errorf(\"artifact %v not staged\", a.Name)\n\t\t}\n\t\tif a.Sha256 == \"\" {\n\t\t\ta.Sha256 = info.hash\n\t\t}\n\t\tif info.hash != a.Sha256 {\n\t\t\treturn nil, errors.Errorf(\"staged artifact for %v has invalid SHA256: %v, want %v\", a.Name, info.hash, a.Sha256)\n\t\t}\n\n\t\tloc = append(loc, &jobpb.ProxyManifest_Location{Name: a.Name, Uri: info.object})\n\t}\n\treturn loc, nil\n}\n\n// PutArtifact stores the given artifact in GCS.\nfunc (s *StagingServer) PutArtifact(ps jobpb.LegacyArtifactStagingService_PutArtifactServer) error {\n\t// Read header\n\n\theader, err := ps.Recv()\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"failed to receive header\")\n\t}\n\tmd := header.GetMetadata().GetMetadata()\n\tif md == nil {\n\t\treturn errors.Errorf(\"expected header as first message: %v\", header)","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/artifact/gcsproxy/staging.go#L94-L130","documentation":"matchLocations compares the staged blob's computed SHA256 against the manifest's declared SHA256. On mismatch the staged content differs from what the manifest expects, so it refuses to emit a location for that artifact.","triggerScenarios":"Calling CommitManifest when an artifact's declared a.Sha256 (non-empty) differs from the hash of the bytes actually uploaded via PutArtifact for the same name.","commonSituations":"Artifact file was modified/rebuilt between manifest generation and staging; corrupt upload; re-uploading different content under the same artifact name.","solutions":["Rebuild the manifest from the current artifact files so declared SHA256 values match","Re-stage the artifact via PutArtifact so the staged blob matches the manifest hash","Verify the artifact file wasn't modified between manifest creation and staging"],"exampleFix":"// before: stale manifest hash\nmanifest.Sha256 = \"aaa...\" // file rebuilt since\n\n// after: regenerate manifest from current files\nmanifest.Sha256 = computeSHA256(artifactPath)","handlingStrategy":"validation","validationCode":"sum := sha256.Sum256(artifactBytes)\nif a.Sha256 != \"\" && hex.EncodeToString(sum[:]) != a.Sha256 {\n    return fmt.Errorf(\"artifact %q hash mismatch before commit\", a.Name)\n}","typeGuard":null,"tryCatchPattern":"if err := CommitManifest(ctx, req); err != nil {\n    if strings.Contains(err.Error(), \"invalid SHA256\") {\n        // regenerate manifest hashes and re-stage\n    }\n    return err\n}","preventionTips":["Generate the manifest from the exact artifact files you will stage","Recompute SHA256 whenever artifacts are rebuilt","Never mutate artifact files after manifest creation"],"tags":["gcs","artifact-staging","sha256","checksum"],"backgroundTag":"checksum-mismatch","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}