nsqio/nsq · critical

failed to lock data-path: %v

Error message

failed to lock data-path: %v

What it means

nsqd takes an exclusive flock (LOCK_EX|LOCK_NB, see internal/dirlock) on its data directory at startup to guarantee a single writer per --data-path. This error means the lock could not be acquired: most commonly another live nsqd already owns the directory (the underlying error text says 'possibly in use by another instance of nsqd'), or the directory could not even be opened (missing, not a directory, no permissions).

Source

Thrown at nsqd/nsqd.go:108

		startTime:            time.Now(),
		topicMap:             make(map[string]*Topic),
		exitChan:             make(chan int),
		notifyChan:           make(chan interface{}),
		optsNotificationChan: make(chan struct{}, 1),
		dl:                   dirlock.New(dataPath),
	}
	n.ctx, n.ctxCancel = context.WithCancel(context.Background())
	httpcli := http_api.NewClient(nil, opts.HTTPClientConnectTimeout, opts.HTTPClientRequestTimeout)
	n.ci = clusterinfo.New(n.logf, httpcli)

	n.lookupPeers.Store([]*lookupPeer{})

	n.swapOpts(opts)
	n.errValue.Store(errStore{})

	err = n.dl.Lock()
	if err != nil {
		return nil, fmt.Errorf("failed to lock data-path: %v", err)
	}

	if opts.MaxDeflateLevel < 1 || opts.MaxDeflateLevel > 9 {
		return nil, errors.New("--max-deflate-level must be [1,9]")
	}

	if opts.ID < 0 || opts.ID >= 1024 {
		return nil, errors.New("--node-id must be [0,1024)")
	}

	if opts.TLSClientAuthPolicy != "" && opts.TLSRequired == TLSNotRequired {
		opts.TLSRequired = TLSRequired
	}

	tlsConfig, err := buildTLSConfig(opts)
	if err != nil {
		return nil, fmt.Errorf("failed to build TLS config - %s", err)
	}

View on GitHub (pinned to 85cf10c09c)

Solutions

  1. Find the other holder: pgrep -a nsqd, fuser -v /var/lib/nsqd, then stop it or give this instance its own --data-path
  2. Verify the directory exists, is a directory, and is readable/writable by the nsqd user: ls -ld /var/lib/nsqd
  3. If using restart wrappers, add a pre-start check that waits for the old pid to exit
  4. In containers, ensure the volume is mounted before nsqd starts

Example fix

# before (two instances, same path)
nsqd --data-path=/var/lib/nsqd
nsqd --data-path=/var/lib/nsqd   # second start -> failed to lock data-path

# after
nsqd --data-path=/var/lib/nsqd-a
nsqd --data-path=/var/lib/nsqd-b
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-start: ensure path exists and is not already locked
if fi, err := os.Stat(dataPath); err != nil || !fi.IsDir() {
	log.Fatalf("data path %s missing or not a directory", dataPath)
}

Try / catch

n, err := nsqd.New(opts)
if err != nil {
	if strings.Contains(err.Error(), "failed to lock data-path") {
		// another nsqd owns the directory (or path unopenable):
		// stop the other instance or use a distinct --data-path; do not retry in place
	}
	log.Fatal(err)
}

Prevention

When it happens

Trigger: Starting a second nsqd with the same --data-path; the directory does not exist or nsqd's user lacks read permission on it; a supervising system (systemd restart, Docker retry) launching a replacement before the old process fully exited and released the flock.

Common situations: Copy-pasting a unit file for a second node without changing --data-path; accidental double-start via service manager plus manual run; k8s liveness probe storm causing overlapping restarts; moving data-path to a mount that is not yet mounted (path missing).

Related errors


AI-assisted analysis of nsqio/nsq@85cf10c09c (2026-08-16). Data as JSON: /api/errors/78604c553183a732. Report an issue: GitHub.