abhigyanpatwari/GitNexus · error · Error
Unable to determine process start time for file lock owner p
Error message
Unable to determine process start time for file lock owner pid ${owner.pid}. What it means
acquireFileLock builds a FileLockOwner record that must include the owning process's start time, which is used later to detect stale locks (a PID reuse guard). It reads the start time from options.processStartTime or falls back to readProcessStartTime(pid); if both yield nothing, it throws because a lock without a start time cannot be safely reclaimed by other processes.
Source
Thrown at gitnexus/src/storage/file-lock.ts:53
/** Acquire a recoverable cross-process mutex using an atomically published owner file. */
export async function acquireFileLock(
lockPath: string,
options: FileLockOptions = {},
): Promise<() => Promise<void>> {
const resolvedPath = path.resolve(lockPath);
const retries = options.retries ?? 0;
const retryDelayMs = options.retryDelayMs ?? 50;
const pid = options.pid ?? process.pid;
const owner: FileLockOwner = {
pid,
ownerId: crypto.randomUUID(),
processStartTime:
options.processStartTime ?? (options.readProcessStartTime ?? readProcessStartTime)(pid) ?? '',
hostname: options.hostname ?? HOSTNAME,
};
if (!owner.processStartTime) {
throw new Error(`Unable to determine process start time for file lock owner pid ${owner.pid}.`);
}
await fs.mkdir(path.dirname(resolvedPath), { recursive: true });
const pendingPath = `${resolvedPath}.pending-${owner.ownerId}`;
await fs.writeFile(pendingPath, `${JSON.stringify(owner)}\n`, { encoding: 'utf-8', flag: 'wx' });
try {
for (let attempt = 0; ; attempt += 1) {
try {
await fs.link(pendingPath, resolvedPath);
break;
} catch (error) {
if (!(await isLockConflict(error, resolvedPath))) throw error;
if (
await reclaimStaleLock(
resolvedPath,
owner,
options.isProcessAlive ?? isProcessAlive,View on GitHub (pinned to 0d1aed942f)
Solutions
- Pass options.processStartTime explicitly (e.g. read it yourself via `ps -p <pid> -o lstart=` or /proc/<pid>/stat field 22) when the automatic lookup is unavailable.
- Ensure the pid passed via options.pid is a live process on the current machine.
- Check that utils/process-identity readProcessStartTime supports the platform; supply a custom readProcessStartTime in options for unsupported platforms.
- If this occurs inside reclaimStaleLock, confirm the caller supplied a valid guardOwner.processStartTime.
Example fix
// before
await acquireFileLock(lockPath, { pid: workerPid });
// after
await acquireFileLock(lockPath, { pid: workerPid, processStartTime: getStartTime(workerPid) }); Defensive patterns
Strategy: try-catch
Validate before calling
const startTime = options.processStartTime ?? readProcessStartTime(pid);
if (!startTime) throw new Error(`Cannot acquire lock: no start time for pid ${pid}`); Type guard
function hasProcessStartTime(o: FileLockOptions & { processStartTime?: string }): o is FileLockOptions & { processStartTime: string } {
return typeof o.processStartTime === 'string' && o.processStartTime.length > 0;
} Try / catch
try {
const release = await acquireFileLock(lockPath, options);
// ...
} catch (err) {
if (err instanceof Error && err.message.includes('Unable to determine process start time')) {
// fall back to explicit processStartTime or skip locking
}
throw err;
} Prevention
- Always pass an explicit processStartTime when acquiring locks with a custom pid.
- Test lock acquisition on every target platform (not all expose process start times).
- Never pass a dead or foreign pid as options.pid.
- Wrap custom readProcessStartTime implementations so they never return undefined silently — log it.
When it happens
Trigger: Calling acquireFileLock (directly or via acquireWatchLock, resetAutoSyncState, run, reclaimStaleLock, release, nextRelease) when options.processStartTime is undefined and readProcessStartTime(pid) returns undefined — e.g. the pid no longer exists, the OS does not expose /proc-style start-time info (unsupported platform), or a custom readProcessStartTime returns undefined.
Common situations: Running on a platform/container where the start-time lookup fails; passing an options.pid that is not a live process; a mocked or overridden readProcessStartTime (test harness) returning undefined; missing explicit processStartTime in constrained environments.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- ${source}: branch name must not contain whitespace.
- GitNexus could not move the LadybugDB WAL sidecar at ${dbPat
- Timed out after ${waitedMs}ms waiting for another gitnexus a
- Timed out after ${waitedMs}ms waiting for another gitnexus a
- GitNexus: unable to acquire init lock after ${INIT_LOCK_MAX_
AI-assisted analysis of abhigyanpatwari/GitNexus@0d1aed942f (2026-09-08).
Data as JSON: /api/errors/c7e7b77ddcfd92fa.
Report an issue: GitHub.