dgraph-io/dgraph · error

bulk output directory cannot be empty

Error message

bulk output directory cannot be empty

What it means

Import validates that bulkOutDir is non-empty before connecting. The bulk output directory is the root of the exported bulk-loader output whose per-group p/ subdirectories get streamed into the cluster, so an empty path is rejected outright.

Source

Thrown at dgraph/cmd/dgraphimport/import_client.go:45

// newClient creates a new import client with the specified endpoint and gRPC options.
func newClient(connectionString string) (api.DgraphClient, error) {
	if connectionString == "" {
		return nil, fmt.Errorf("connection string cannot be empty")
	}

	dg, err := dgo.Open(connectionString)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to endpoint [%s]: %w", connectionString, err)
	}

	glog.Infof("[import] Successfully connected to Dgraph endpoint: %s", connectionString)
	return dg.GetAPIClients()[0], nil
}

func Import(ctx context.Context, connectionString string, bulkOutDir string) error {
	if bulkOutDir == "" {
		return fmt.Errorf("bulk output directory cannot be empty")
	}

	dg, err := newClient(connectionString)
	if err != nil {
		return err
	}
	resp, err := initiateSnapshotStream(ctx, dg)
	if err != nil {
		return err
	}

	return streamSnapshot(ctx, dg, bulkOutDir, resp.Groups)
}

// isRetryableError returns true for transient errors that may resolve after a brief wait,
// such as Raft proposal backlogs during membership changes.
func isRetryableError(err error) bool {
	if err == nil {

View on GitHub (pinned to 759e242be6)

Solutions

  1. Pass the path to the bulk output directory (the one containing numeric per-group subdirectories with p/ inside).
  2. Validate the flag/env before calling Import.
  3. Ensure the bulk export step actually ran and its output path was captured.
  4. Check the directory layout matches group-ID subdirectories (e.g. 0/p, 1/p) after fixing the path.

Example fix

// before
err := dgraphimport.Import(ctx, addr, cfg.BulkDir) // cfg.BulkDir == ""
// after
if cfg.BulkDir == "" { return errors.New("bulk output directory is required") }
err := dgraphimport.Import(ctx, addr, cfg.BulkDir)
Defensive patterns

Strategy: validation

Validate before calling

if err := validateBulkDir(bulkDir); err != nil { return err }

func validateBulkDir(dir string) error {
    if strings.TrimSpace(dir) == "" {
        return errors.New("bulk output directory is required")
    }
    fi, err := os.Stat(dir)
    if err != nil || !fi.IsDir() {
        return fmt.Errorf("bulk output dir %q not found", dir)
    }
    return nil
}

Type guard

func validBulkOutDir(s string) bool { return strings.TrimSpace(s) != "" }

Try / catch

if err := dgraphimport.Import(ctx, addr, bulkDir); err != nil {
    if err.Error() == "bulk output directory cannot be empty" {
        return fmt.Errorf("config error: bulk output dir flag/env not set")
    }
    return err
}

Prevention

When it happens

Trigger: Calling dgraphimport.Import with bulkOutDir == "" — unset flag/env/config for the bulk output location.

Common situations: Forgot to pass the directory produced by `dgraph bulk`, pipeline variable empty in CI, running Import programmatically with a placeholder not yet filled.

Related errors


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