dgraph-io/dgraph · error
downstream(%d) closed before Finish=true
Error message
downstream(%d) closed before Finish=true
What it means
After half-closing the downstream send, the relay drains the leader's responses waiting for the frame with Finish=true. If out.Recv() returns io.EOF instead, the leader closed its side without signalling completion, so the import result is unknown and this error is returned.
Source
Thrown at worker/import.go:427
if req.Pkt == nil {
return fmt.Errorf("unexpected empty request")
}
if req.Pkt.Done {
// Forward Done, half-close downstream send.
if err := out.Send(&api.StreamExtSnapshotRequest{Pkt: req.Pkt}); err != nil && !errors.Is(err, io.EOF) {
return fmt.Errorf("send done downstream(%d): %w", groupId, err)
}
_ = out.CloseSend()
// Drain downstream and relay upstream until Finish=true.
for {
if err := ctx.Err(); err != nil {
return err
}
resp, err := out.Recv()
if errors.Is(err, io.EOF) {
return fmt.Errorf("downstream(%d) closed before Finish=true", groupId)
}
if err != nil {
return fmt.Errorf("recv final downstream(%d): %w", groupId, err)
}
if err := in.Send(resp); err != nil {
return fmt.Errorf("relay final upstream: %w", err)
}
if resp.Finish {
glog.Infof("[import] [forward %d -> %d] finish", currentGroup, groupId)
return nil
}
}
}
// Normal data chunk: send -> wait ack -> send upstream ack.
if err := out.Send(&api.StreamExtSnapshotRequest{Pkt: req.Pkt}); err != nil {
return fmt.Errorf("send data downstream(%d): %w", groupId, err)
}View on GitHub (pinned to 759e242be6)
Solutions
- Check the leader's logs for the error that ended its handler before Finish
- Re-run the import; the snapshot state may be incomplete
- Ensure the leader has adequate disk/memory for finalizing an external snapshot
- Remove idle-timeout intermediaries between the two alphas
Defensive patterns
Strategy: retry
Validate before calling
// ensure leader resources before import so finalization can complete precheckDiskAndMemory(leaderHost)
Type guard
func finishedCorrectly(resp *api.StreamExtSnapshotResponse) bool {
return resp != nil && resp.Finish // only Finish=true means success
} Try / catch
if err != nil && strings.Contains(err.Error(), "closed before Finish=true") {
// outcome unknown: check leader logs, then re-run the import
} Prevention
- Never kill or upgrade the leader mid-import
- Give the leader disk/memory headroom for finalization
- Remove proxies that close quiet connections during final flush
- Treat any Finish-less close as a failed import and retry
When it happens
Trigger: The leader's StreamExtSnapshot server handler returned or closed the stream before sending Finish=true: it errored on its own snapshot pipeline, was killed, or its context was cancelled.
Common situations: Leader hit an internal error (disk full, memory) mid-finalization; leader restarted at the very end of the import; a proxy closed the connection after inactivity during the leader's final flush.
Related errors
- unexpected empty request
- while calling MovePredicate
- failed to establish stream with leader: %v
- failed to send forward request: %v
- recv upstream(%d): %w
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/f22121962719395e.
Report an issue: GitHub.