{"record":{"id":"3bfe7aa82e5e491d","repo":"laurent22/joplin","slug":"synchronisation-is-already-in-progress","errorCode":null,"errorMessage":"Synchronisation is already in progress.","messagePattern":"Synchronisation is already in progress\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/app-cli/app/command-sync.ts","lineNumber":147,"sourceCode":"\t\t}\n\t}\n\n\tpublic doingAuth() {\n\t\treturn !!this.oneDriveApiUtils_;\n\t}\n\n\tpublic async action(args: { options: { useLock?: number; target?: number; upgrade?: boolean } }) {\n\t\tthis.releaseLockFn_ = null;\n\n\t\t// Lock is unique per profile/database\n\t\tconst lockFilePath = `${require('os').tmpdir()}/synclock_${md5(escape(Setting.value('profileDir')))}`; // https://github.com/pvorb/node-md5/issues/41\n\t\tif (!(await pathExists(lockFilePath))) await writeFile(lockFilePath, 'synclock');\n\n\t\tconst useLock = args.options.useLock !== 0;\n\n\t\tif (useLock) {\n\t\t\ttry {\n\t\t\t\tif (await Command.isLocked(lockFilePath)) throw new Error(_('Synchronisation is already in progress.'));\n\n\t\t\t\tthis.releaseLockFn_ = await Command.lockFile(lockFilePath);\n\t\t\t} catch (error) {\n\t\t\t\tif (error.code === 'ELOCKED') {\n\t\t\t\t\tconst msg = _('Lock file is already being hold. If you know that no synchronisation is taking place, you may delete the lock file at \"%s\" and resume the operation.', error.file);\n\t\t\t\t\tthis.stdout(msg);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tconst cleanUp = () => {\n\t\t\tcliUtils.redrawDone();\n\t\t\tif (this.releaseLockFn_) {\n\t\t\t\tthis.releaseLockFn_();\n\t\t\t\tthis.releaseLockFn_ = null;\n\t\t\t}","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-cli/app/command-sync.ts#L129-L165","documentation":"Thrown by `sync` when `Command.isLocked(lockFilePath)` returns true, signalling another process already holds the per-profile sync lock (a proper-lockfile lock on `${tmpdir}/synclock_<md5(profileDir)>`). The intent is to prevent two concurrent synchronizers from corrupting the same database. Note: this manually-thrown Error has no `.code`, so the surrounding catch (which only special-cases ELOCKED) re-throws it to the caller rather than printing the friendlier lock-held message.","triggerScenarios":"Running `:sync` (or auto-sync) while another Joplin process or another `:sync` invocation for the same profile is already mid-sync. Also after a crashed prior sync that left a stale lock file within the 5-minute `stale` window, though proper-lockfile normally releases on process exit.","commonSituations":"Two terminals running `:sync` against one profile; the desktop app and CLI sharing a profile; a previous sync crashed and the OS has not reaped the lock; CI invoking sync in parallel jobs on one profile dir.","solutions":["Wait for the in-progress sync to finish, then retry — concurrency is the intended block.","If you are certain no sync is running, remove the stale lock file at the path printed in the ELOCKED message (or `${tmpdir}/synclock_<md5(profileDir)>`) and retry.","Run different profiles from different processes instead of sharing one profile across concurrent invocations.","As a last resort pass `--use-lock 0` to disable local locking (only when you can guarantee single-writer access)."],"exampleFix":"// before\nif (await Command.isLocked(lockFilePath)) throw new Error(_('Synchronisation is already in progress.'));\n\n// after - attach a stable code so callers/the catch can branch on it\nif (await Command.isLocked(lockFilePath)) {\n\tconst e = new Error(_('Synchronisation is already in progress.'));\n\t(e as any).code = 'SYNC_IN_PROGRESS';\n\tthrow e;\n}","handlingStrategy":"retry","validationCode":"// Check the lock before attempting sync.\nconst lockFilePath = `${require('os').tmpdir()}/synclock_${md5(escape(Setting.value('profileDir')))}`;\nif (await locker.check(lockFilePath)) {\n\tthrow new Error('Sync already in progress on this profile; wait or remove the stale lock.');\n}","typeGuard":"null","tryCatchPattern":"try {\n\tawait sync();\n} catch (e) {\n\tif (/Synchronisation is already in progress/.test(e.message)) {\n\t\t// transient — wait for the other sync then retry once\n\t\tawait new Promise(r => setTimeout(r, 5000));\n\t\tawait sync();\n\t} else throw e;\n}","preventionTips":["Never run two sync writers against one profile concurrently.","If a prior sync crashed, remove the stale lock file before retrying.","Use separate profiles for parallel automation instead of sharing one."],"tags":["sync","lock","concurrency","cli","retryable"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}