dgraph-io/dgraph · error
UpdateExtSnapshotStreamingStateRequest cannot have both Star
Error message
UpdateExtSnapshotStreamingStateRequest cannot have both Start and Finish set to true
What it means
The Start and Finish flags of UpdateExtSnapshotStreamingStateRequest are mutually exclusive: Start turns import/streaming mode on, Finish turns it off. Setting both is ambiguous and rejected with this error before any Raft proposal is made.
Source
Thrown at worker/import.go:463
}
if _, err := out.Recv(); err != nil {
return fmt.Errorf("ack data downstream(%d): %w", groupId, err)
}
if err := in.Send(&api.StreamExtSnapshotResponse{}); err != nil {
return fmt.Errorf("send ack upstream: %w", err)
}
}
}
func (w *grpcWorker) UpdateExtSnapshotStreamingState(ctx context.Context,
req *api.UpdateExtSnapshotStreamingStateRequest) (*pb.Status, error) {
if req == nil {
return nil, errors.New("UpdateExtSnapshotStreamingStateRequest must not be nil")
}
if req.Start && req.Finish {
return nil, errors.New("UpdateExtSnapshotStreamingStateRequest cannot have both Start and Finish set to true")
}
glog.Infof("[import] Applying import mode proposal: %+v", req)
err := groups().Node.proposeAndWait(ctx, &pb.Proposal{ExtSnapshotState: req})
return &pb.Status{}, err
}
// StreamExtSnapshot handles the stream of key-value pairs sent from proxy alpha.
// It receives a Forward flag from the stream to determine if the current node is the leader.
// If the node is the leader (Forward is true), it streams the data to its followers.
// Otherwise, it simply writes the data to BadgerDB and flushes it.
func (w *grpcWorker) StreamExtSnapshot(stream pb.Worker_StreamExtSnapshotServer) error {
glog.Info("[import] trying to update the import mode to false")
defer x.ExtSnapshotStreamingState(false)
// Receive the first message to check the Forward flag.
// If Forward is true, this node is the leader and should forward the stream to its followers.View on GitHub (pinned to 759e242be6)
Solutions
- Send two separate calls: one with Start=true, later one with Finish=true.
- Reset the struct before reuse: &api.UpdateExtSnapshotStreamingStateRequest{Start: true}.
- Validate flags client-side before invoking the RPC.
Example fix
// before
req := &api.UpdateExtSnapshotStreamingStateRequest{Start: prev.Finish}
req.Start, req.Finish = true, true
// after
req := &api.UpdateExtSnapshotStreamingStateRequest{Finish: true} Defensive patterns
Strategy: validation
Validate before calling
if req != nil && req.Start && req.Finish {
return errors.New("Start and Finish are mutually exclusive; send two separate requests")
} Type guard
func isAmbiguousStateReq(req *api.UpdateExtSnapshotStreamingStateRequest) bool {
return req != nil && req.Start && req.Finish
} Try / catch
if _, err := wc.UpdateExtSnapshotStreamingState(ctx, req); err != nil {
if strings.Contains(err.Error(), "both Start and Finish") { /* send separate Start and Finish calls */ }
} Prevention
- Send Start and Finish as two separate RPC calls
- Reset reused request structs field-by-field instead of mutating old ones
- Validate mutually exclusive flags client-side before RPCs
When it happens
Trigger: Sending an UpdateExtSnapshotStreamingStateRequest with both Start=true and Finish=true via the gRPC API, e.g. reusing a populated struct and only flipping one flag.
Common situations: Copy/reuse of a previous request struct without resetting fields; buggy client code computing flags; a mistaken attempt to 'restart' streaming in one call.
Related errors
- connection string cannot be empty
- Tablets are empty in %+v
- Group ID is Zero in %+v
- Tablet predicate is empty in %+v
- UpdateExtSnapshotStreamingStateRequest must not be nil
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/b610fd54dd4947cf.
Report an issue: GitHub.