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.
Problem
Running multiple OpenCode instances from the same directory fails with:
The
acquirePersistenceLeasecall inensureSessionLoaded(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.
File:
src/goal-plugin.js, functionensureSessionLoaded, line ~3106.Trade-offs
acquireMigrationLease, which already retries gracefully on the same error (line ~1488).Alternative
Allow configuring a per-instance
stateFilePathvia 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.