dgraph-io/dgraph · error

UpdateExtSnapshotStreamingStateRequest must not be nil

Error message

UpdateExtSnapshotStreamingStateRequest must not be nil

What it means

UpdateExtSnapshotStreamingState is a gRPC admin operation that arms (or disarms) external-snapshot import mode — a destructive operation that replaces a group store. The server rejects a nil request pointer outright before any authorization or Raft proposal (edgraph/server.go:2058). This guards the handler against malformed gRPC calls where the request body was never populated.

Source

Thrown at edgraph/server.go:2058

	if !x.WorkerConfig.AclEnabled {
		return nil
	}

	ns, err := x.ExtractNamespaceFrom(ctx)
	if err != nil {
		return err
	}
	if tc.Hash != getHash(ns, tc.StartTs) {
		return x.ErrHashMismatch
	}
	return nil
}

func (s *Server) UpdateExtSnapshotStreamingState(ctx context.Context,
	req *api.UpdateExtSnapshotStreamingStateRequest) (v *api.UpdateExtSnapshotStreamingStateResponse, err error) {

	if req == nil {
		return nil, errors.New("UpdateExtSnapshotStreamingStateRequest must not be nil")
	}

	// External-snapshot import is a destructive admin operation: it arms import mode and
	// (via StreamExtSnapshot) replaces a group store. Gate it on both authorization paths so
	// it is protected under ACL and under an --security auth-token. Each gate fails open when
	// its feature is unconfigured, so the arming requirement on the stream path backstops the
	// bare-OSS case.
	if err := AuthorizeGuardians(ctx); err != nil {
		return nil, err
	}
	if err := hasPoormansAuth(ctx); err != nil {
		return nil, err
	}

	if req.Start && req.Finish {
		return nil, errors.New("UpdateExtSnapshotStreamingStateRequest cannot have both Start and Finish set to true")
	}

View on GitHub (pinned to 759e242be6)

Solutions

  1. Construct and pass a valid api.UpdateExtSnapshotStreamingStateRequest with Start or Finish set (exactly one of them)
  2. Use the official Dgraph Go client (dgo) admin APIs rather than invoking the raw gRPC method manually
  3. Check your proxy/interceptor layer for dropped request bodies if you believe you sent a request
  4. If using generated stubs, verify the field is populated before Dial/Invoke

Example fix

// before
conn.UpdateExtSnapshotStreamingState(ctx, nil)
// after
req := &api.UpdateExtSnapshotStreamingStateRequest{Start: true}
resp, err := conn.UpdateExtSnapshotStreamingState(ctx, req)
Defensive patterns

Strategy: validation

Validate before calling

func validExtSnapReq(r *api.UpdateExtSnapshotStreamingStateRequest) bool {
	return r != nil
}
// guard before RPC:
// if !validExtSnapReq(req) { return errors.New("request required") }

Type guard

func isNonNilReq(r *api.UpdateExtSnapshotStreamingStateRequest) bool {
	return r != nil
}

Try / catch

resp, err := client.UpdateExtSnapshotStreamingState(ctx, req)
if err != nil && strings.Contains(err.Error(), "must not be nil") {
	// client bug: construct the request and retry once
}

Prevention

When it happens

Trigger: Calling the UpdateExtSnapshotStreamingState gRPC method with a nil request message — typically a hand-rolled client invoking the RPC without constructing api.UpdateExtSnapshotStreamingStateRequest, or a wrapper passing nil through.

Common situations: Custom admin scripts or generated client misuse where the request struct is omitted; middleware/proxy layers forwarding empty bodies; SDK code paths where optional request arguments default to nil.

Related errors


AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01). Data as JSON: /api/errors/503eaa6d6200565d. Report an issue: GitHub.