langgenius/dify · warning · ConcurrentAccessError
concurrent access detected
Error message
concurrent access detected
What it means
ConcurrentAccessError (a BaseError, ClientError → exit 1) from FileBasedStore.lock (store.ts:117) when the lockfile call rejects with errno EEXIST — meaning another process holds `${filePath}.lock`. The lock has a 30s stale threshold (LOCK_STALE_MS), so a crashed process's lock auto-expires after 30s. The error message includes the file path and instructs removing the .lock file. Operations go through withLock (get/set/unset) and getTyped/setTyped, so any store read or write can hit this.
Source
Thrown at cli/src/store/store.ts:117
/* tmp may not exist */
}
throw err
}
}
this.dirty = false
}
async lock(): Promise<void> {
await this.ensureDir()
try {
await lockAsync(`${this.filePath}.lock`, {
stale: LOCK_STALE_MS,
})
} catch (err) {
const code = (err as NodeJS.ErrnoException).code
if (code === 'EEXIST') {
throw new ConcurrentAccessError(this.filePath)
}
throw err
}
}
async load(): Promise<void> {
try {
this.rawContent = await fsp.readFile(this.filePath, 'utf8')
this.dirty = false
} catch (err) {
const code = (err as NodeJS.ErrnoException).code
if (code !== 'ENOENT') {
throw err
}
}
}
public setRawContent(content: string): void {View on GitHub (pinned to ef8544b173)
Solutions
- Wait briefly and retry — if the other process is healthy it will release the lock; if it crashed, the 30s stale timer will reap it.
- Confirm no difyctl is actually running: `ps aux | grep difyctl`. If none, the lock is stale.
- Remove the lockfile named in the error: `rm <filePath>.lock` (the hint says exactly this).
- For shared CI HOME: give each job an isolated HOME or XDG_CONFIG_HOME so they don't share the lock.
- Avoid running concurrent mutating difyctl commands against the same profile.
Example fix
# before — second command fails (difyctl auth login &) ; difyctl use workspace ws_x # races on hosts.yml # after — serialize, or isolate # option 1: wait for the first to finish, then run the second # option 2: separate HOMEs HOME=/tmp/dify-a difyctl auth login HOME=/tmp/dify-b difyctl use workspace ws_x # option 3: clear a confirmed-stale lock rm ~/.config/difyctl/hosts.yml.lock
Defensive patterns
Strategy: try-catch
Validate before calling
// before mutating, ensure no other difyctl holds the lock by checking for the lockfile
import { stat } from 'node:fs/promises'
async function isLockStale(lockPath: string, staleMs = 30_000): Promise<boolean> {
try {
const s = await stat(lockPath)
return Date.now() - s.mtimeMs > staleMs
} catch {
return false // no lockfile → not stale
}
} Try / catch
import { ConcurrentAccessError } from '@/store/errors'
try {
await reg.save()
} catch (err) {
if (err instanceof ConcurrentAccessError) {
// the error message names the .lock file to remove
console.error(err.message)
// optionally: surface to the user and retry once after they confirm
throw err
}
throw err
} Prevention
- Serialize difyctl invocations that share the same HOME/config dir.
- Give parallel CI jobs isolated HOME or XDG_CONFIG_HOME so they don't share locks.
- Don't SIGKILL difyctl — let it exit cleanly so it releases the lock.
- If a lock is confirmed stale (older than 30s, no difyctl running), remove the named .lock file.
When it happens
Trigger: Two difyctl invocations racing on the same hosts.yml (or other store file): a background script and an interactive session, or two CI jobs sharing a HOME. Also a previous difyctl killed with SIGKILL leaving a lock younger than 30s. The lock is per-file (`${filePath}.lock`), so concurrent commands touching different files don't interfere.
Common situations: CI runner that reuses HOME across parallel jobs; a long-running watch loop (`difyctl use` in one pane, `difyctl auth login` in another); a hung process that didn't exit; NFS/home with flaky locking; the lockfile itself left behind by a kernel OOM kill.
Related errors
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/5ff55e90ffc50324.
Report an issue: GitHub.