Skip to content

Gracefully handle contested persistence lock when running multiple instances #41

Description

@hugwi

Problem

Running multiple OpenCode instances from the same directory fails with:

ERROR goal persistence is already owned by pid <PID> on <hostname>;
close the other OpenCode instance or configure a different stateFilePath

The acquirePersistenceLease call in ensureSessionLoaded (goal-plugin.js:3106) throws a hard error when another instance already holds the lock. This makes it impossible to run multiple instances from the same working directory — a common workflow.

Proposed Fix

Wrap the lease acquisition in a try/catch and return gracefully when the lock is contested. The session starts normally; goal persistence simply skips for that instance while the lock-holding instance continues to persist.

-      const lease = await acquirePersistenceLease(paths.stateFilePath)
+      let lease
+      try {
+        lease = await acquirePersistenceLease(paths.stateFilePath)
+      } catch (leaseError) {
+        if (String(leaseError?.message || leaseError).includes("goal persistence is already owned")) {
+          // Another instance holds the lock — continue without persistence.
+          return true
+        }
+        throw leaseError
+      }

File: src/goal-plugin.js, function ensureSessionLoaded, line ~3106.

Trade-offs

  • The non-lock-holding instance runs without goal persistence (goals won't be saved to disk for that session).
  • The lock-holding instance persists normally.
  • This matches the existing pattern in acquireMigrationLease, which already retries gracefully on the same error (line ~1488).

Alternative

Allow configuring a per-instance stateFilePath via env var (OPENCODE_GOAL_STATE_PATH) so each instance gets its own state root. The plugin already supports this option, but it requires manual setup. The graceful-fail approach is a better default UX.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions