wavetermdev/waveterm · error

filemutex trylock error: %w

Error message

filemutex trylock error: %w

What it means

AcquireWaveLock in pkg/wavebase wraps a failure from filemutex.New's TryLock() — the process could not obtain an exclusive OS-level file lock on the Wave lock file. TryLock returns a non-nil error when the lock file is already held by another process (or an OS-level locking failure occurs). It is thrown at startup so the app never runs two instances against the same data directory.

Source

Thrown at pkg/wavebase/wavebase-win.go:26

import (
	"fmt"
	"log"
	"path/filepath"

	"github.com/alexflint/go-filemutex"
)

func AcquireWaveLock() (FDLock, error) {
	dataHomeDir := GetWaveDataDir()
	lockFileName := filepath.Join(dataHomeDir, WaveLockFile)
	log.Printf("[base] acquiring lock on %s\n", lockFileName)
	m, err := filemutex.New(lockFileName)
	if err != nil {
		return nil, fmt.Errorf("filemutex new error: %w", err)
	}
	err = m.TryLock()
	if err != nil {
		return nil, fmt.Errorf("filemutex trylock error: %w", err)
	}
	return m, nil
}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Check for and close any already-running Wave process, then retry launch
  2. Inspect the wrapped error: if it indicates the lock is simply held, that is the single-instance guard working — reuse the running instance
  3. Verify the lock file directory is writable and on a filesystem supporting file locks (not NFS/SMB)
  4. If a dead process left the file, delete the stale lock file (the lock itself is released by the OS when the process dies)

Example fix

// before: app exits with 'filemutex trylock error: resource temporarily unavailable'
// after: detect double-launch before calling
if !canAcquireLock(lockFileName) {
    fmt.Println("Wave is already running; exiting")
    os.Exit(0)
}
lock, err := wavebase.AcquireWaveLock(lockFileName)
Defensive patterns

Strategy: try-catch

Validate before calling

// before launching
if _, err := os.Stat(lockFilePath); err == nil {
    if procRunning('waveterm') { fmt.Println("already running"); os.Exit(0) }
}

Try / catch

lock, err := wavebase.AcquireWaveLock(name)
if err != nil {
    if strings.Contains(err.Error(), "trylock") {
        log.Fatalf("another instance holds the lock: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling AcquireWaveLock (typically from main) when another Wave process already holds TryLock on lockFileName; or the underlying syscall (flock/LockFileEx) fails due to filesystem/permissions issues.

Common situations: Launching Wave while an existing instance is still running; a stale/crashed process holding the lock; running from a network or read-only filesystem where locking is unsupported; permission problems on the lock file.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/36a0c74fb076c1c9. Report an issue: GitHub.