{"record":{"id":"6062435f886544e9","repo":"dgraph-io/dgraph","slug":"failed-to-send-done-signal-for-group-d-w","errorCode":null,"errorMessage":"failed to send 'done' signal for group [%d]: %w","messagePattern":"failed to send 'done' signal for group \\[(.+?)\\]: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"dgraph/cmd/dgraphimport/import_client.go","lineNumber":242,"sourceCode":"\t\tif _, err := out.Recv(); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to receive response for group ID [%v] from the server: %w\", groupId, err)\n\t\t}\n\t\tglog.Infof(\"[import] Group [%v]: Received ACK for sending data chunk\", groupId)\n\n\t\treturn nil\n\t}\n\n\t// Execute the stream process\n\tif err := stream.Orchestrate(ctx); err != nil {\n\t\treturn fmt.Errorf(\"stream orchestration failed for group [%v]: %w, badger path: %s\", groupId, err, ps.Opts().Dir)\n\t}\n\n\t// Send the final 'done' signal to mark completion\n\tglog.Infof(\"[import] Sending completion signal for group [%d]\", groupId)\n\tdone := &api.StreamPacket{Done: true}\n\n\tif err := out.Send(&api.StreamExtSnapshotRequest{Pkt: done}); err != nil && !errors.Is(err, io.EOF) {\n\t\treturn fmt.Errorf(\"failed to send 'done' signal for group [%d]: %w\", groupId, err)\n\t}\n\n\tfor {\n\t\tif ctx.Err() != nil {\n\t\t\treturn ctx.Err()\n\t\t}\n\t\tresp, err := out.Recv()\n\t\tif errors.Is(err, io.EOF) {\n\t\t\treturn fmt.Errorf(\"server closed stream before Finish=true for group [%d]\", groupId)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to receive final response for group ID [%v] from the server: %w\", groupId, err)\n\t\t}\n\t\tif resp.Finish {\n\t\t\tglog.Infof(\"[import] Group [%v]: Received final Finish=true\", groupId)\n\t\t\tbreak\n\t\t}\n\t\tglog.Infof(\"[import] Group [%v]: Waiting for Finish=true, got interim ACK\", groupId)","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/dgraph/cmd/dgraphimport/import_client.go#L224-L260","documentation":"Returned by streamBadger when sending the final Done packet fails (non-EOF). This packet tells the server the group's snapshot stream is complete; without a successful send the server treats the transfer as incomplete and will not finalize the snapshot.","triggerScenarios":"The gRPC stream already broke during data transfer (server crash, connection reset) so the done Send fails; server closed its side after an earlier error; context cancelled at the very end of a long transfer.","commonSituations":"Import that 'almost finished' but the Alpha restarted or was OOM-killed; LB/proxy dropping the long-lived stream just before completion; cluster churn (leader change) invalidating the stream.","solutions":["Check whether the done Send failed because the stream was already dead — inspect earlier gRPC errors and server logs.","Re-run the import for the affected group; a missing done signal means the server never finalized.","Fix stream stability (keepalive, LB timeouts) so the connection survives the whole transfer.","Verify all groups report completion server-side after the import.","Protect the transfer with a context sized for the dataset, not a default short deadline."],"exampleFix":"// before\nif err := out.Send(&api.StreamExtSnapshotRequest{Pkt: done}); err != nil && !errors.Is(err, io.EOF) {\n\treturn fmt.Errorf(\"failed to send 'done' signal for group [%d]: %w\", groupId, err)\n}\n// after\nif err := out.Send(&api.StreamExtSnapshotRequest{Pkt: done}); err != nil && !errors.Is(err, io.EOF) {\n\treturn fmt.Errorf(\"failed to send 'done' signal for group [%d] (transfer incomplete, will need re-run): %w\", groupId, err)\n}","handlingStrategy":"try-catch","validationCode":"select {\ncase <-ctx.Done():\n\treturn fmt.Errorf(\"context ended before done-signal; extend deadline: %w\", ctx.Err())\ndefault:\n}","typeGuard":"func streamAlreadyDead(err error) bool {\n\ts, ok := status.FromError(err)\n\treturn ok && (s.Code() == codes.Unavailable || s.Code() == codes.Canceled)\n}","tryCatchPattern":"if err := out.Send(&api.StreamExtSnapshotRequest{Pkt: done}); err != nil && !errors.Is(err, io.EOF) {\n\tif streamAlreadyDead(err) {\n\t\treturn fmt.Errorf(\"stream died before completion for group %d; full re-import required: %w\", groupId, err)\n\t}\n\treturn fmt.Errorf(\"failed to send 'done' signal for group [%d]: %w\", groupId, err)\n}","preventionTips":["Keep the connection alive for the entire transfer (keepalive, no LB drops).","Avoid Alpha restarts during imports (k8s PDBs, maintenance windows).","Verify server-side completion for every group after import.","Reserve headroom in the context deadline for the final handshake."],"tags":["grpc","network","stream","snapshot-import"],"backgroundTag":"grpc-send-failed","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}