larksuite/cli · error

lockfile: lock already held

Error message

lockfile: lock already held

What it means

Sentinel returned by TryLock when the advisory lockfile already exists and is held by another process; the doc comment marks it as retryable contention. It signals normal concurrency, not corruption - e.g. a second CLI invocation or an already-running event bus owns the lock.

Source

Thrown at internal/lockfile/lockfile.go:21

package lockfile

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"regexp"

	"github.com/larksuite/cli/internal/core"
	"github.com/larksuite/cli/internal/vfs"
)

// safeIDChars strips path-traversal chars from app IDs.
var safeIDChars = regexp.MustCompile(`[^a-zA-Z0-9._-]`)

// ErrHeld signals retryable contention; callers errors.Is to distinguish from real failures.
var ErrHeld = errors.New("lockfile: lock already held")

type LockFile struct {
	path string
	file *os.File
}

func New(path string) *LockFile {
	return &LockFile{path: path}
}

// ForSubscribe sanitises appID against path traversal before forming the lock filename.
func ForSubscribe(appID string) (*LockFile, error) {
	if appID == "" {
		return nil, fmt.Errorf("app ID must not be empty")
	}
	dir := filepath.Join(core.GetConfigDir(), "locks")
	if err := vfs.MkdirAll(dir, 0700); err != nil {
		return nil, fmt.Errorf("create lock dir: %w", err)

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Wait for the current holder to finish and retry the lock acquisition
  2. Treat it as 'already running' and skip spawning a second instance, as busdiscover does for PID files
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at internal/lockfile/lockfile.go:21 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/1db0dfa79abae76b. Report an issue: GitHub.