abhigyanpatwari/GitNexus · error · GroupSyncLockError
timeout
timeout
Error message
Could not acquire the sync lock for group "${path.basename(groupDir)}" (${getGroupSyncLockDir(groupDir)}). ${err.message} Nothing was written and this group was not synced. What it means
withGroupSyncLock failed to acquire the per-group sync lock (`<groupDir>/sync-lock`) because the underlying index-lock wait timed out with a guard timeout (isIndexLockGuardTimeout). Only a group sync ever holds this lock, so the error is worded around what is known: which group, which lock path, and the underlying wait error. Nothing was written and the group was not synced.
Solutions
- Wait for the other sync of this group to finish, then re-run the sync.
- Check for stale locks in the group's sync-lock directory (getGroupSyncLockDir) and remove them only if no sync process is running.
- Ensure only one GitNexus process (MCP server, serve, CLI) syncs the same group at a time.
Example fix
// before: two concurrent syncs of the same group await Promise.all([syncGroup(g), syncGroup(g)]); // after await syncGroup(g); // serialize; or use a shared queue per groupDir
Defensive patterns
Strategy: retry
Validate before calling
import fs from 'node:fs';
// Check no sync lock exists before starting
if (fs.existsSync(groupSyncLockPath)) {
console.warn('Another sync of this group may be running; wait before retrying.');
} Type guard
const isGroupSyncLockError = (e: unknown): e is GroupSyncLockError => e instanceof GroupSyncLockError && e.code === 'timeout';
Try / catch
try {
await withGroupSyncLock(groupDir, () => doSync());
} catch (err) {
if (isGroupSyncLockError(err)) {
console.error(`Group ${err.groupDir} is being synced elsewhere; nothing was written.`);
} else throw err;
} Prevention
- Serialize group syncs through a single process or job queue.
- Add retry-with-backoff around syncs that may contend for the same group.
- After crashes, verify no sync process is alive before manually removing sync-lock files.
When it happens
Trigger: Calling a group sync API wrapped in withGroupSyncLock while another sync of the same group is in progress (or a stale lock from a crashed sync exists), and the IndexLockTimeoutError is classified as a guard timeout.
Common situations: Two concurrent `gitnexus` syncs targeting the same group (e.g. MCP server and CLI both syncing); a previous sync crashed leaving a lock file behind; slow disk making the guard wait exceed its budget.
Related errors
- Index lock acquisition timed out
- GitNexus: unable to acquire init lock after
- ${message}
- Timed out after ms waiting for another gitnexus analyze…
- Timed out after ms waiting for another gitnexus analyze…
AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15).
Data as JSON: /api/errors/812104f72d011ba8.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/core/group/group-lock.ts:154
timeoutMs: GROUP_SYNC_LOCK_TIMEOUT_MS,
// `acquireIndexLock`'s own `log` texts name an "analyze" holder, which
// misattributes a group-sync wait — the same reason `withRegistryLock`
// supplies its own line instead of passing `log` through.
onWaitStart: () =>
logger.info(
{ groupDir },
'Waiting for another GitNexus process to finish syncing this group…',
),
});
} catch (err) {
// The inherited message names "another gitnexus analyze" as the holder —
// a cause this detection path cannot establish. Nothing but a group sync
// ever locks `<groupDir>/sync-lock` (see the module header), and on the
// socket backend the holder is not identifiable at all. Re-word it around
// what IS known: which group, which operation, and how long we waited.
if (err instanceof IndexLockTimeoutError) {
if (isIndexLockGuardTimeout(err)) {
throw new GroupSyncLockError(
'timeout',
groupDir,
`Could not acquire the sync lock for group "${path.basename(groupDir)}" ` +
`(${getGroupSyncLockDir(groupDir)}). ${err.message} ` +
`Nothing was written and this group was not synced.`,
err,
);
}
throw new GroupSyncLockError(
'timeout',
groupDir,
`Timed out after ${Date.now() - acquireStartedAt}ms waiting for the sync lock on ` +
`group "${path.basename(groupDir)}" (${getGroupSyncLockDir(groupDir)}). ` +
// `holderKnown` is false on the socket backend and on the file
// backend's malformed/vanished-lock timeouts, where `holder` is a
// placeholder (`pid -1`). Presenting that as a real owner would be the
// same unestablished claim in a new form.
(err.holderKnownView on GitHub (pinned to ac9a4e9abd)