laurent22/joplin · warning · Error

Timed out trying to get sync target clock time

Error message

Timed out trying to get sync target clock time

What it means

Thrown by fetchRemoteDateOffset_() in the file API. It writes a temp file, then polls stat() in a 200ms loop for up to 5 seconds waiting for the target to acknowledge it. If the file never becomes visible, the remote clock offset can't be computed and the driver gives up. The outer remoteDate() catches this and falls back to the device clock.

Source

Thrown at packages/lib/file-api.ts:237

		return !!this.driver().supportsLocks;
	}

	private async fetchRemoteDateOffset_() {
		const tempFile = `${this.tempDirName()}/timeCheck${Math.round(Math.random() * 1000000)}.txt`;
		const startTime = Date.now();
		await this.put(tempFile, 'timeCheck');

		// Normally it should be possible to read the file back immediately but
		// just in case, read it in a loop.
		const loopStartTime = Date.now();
		let stat = null;
		while (Date.now() - loopStartTime < 5000) {
			stat = await this.stat(tempFile);
			if (stat) break;
			await time.msleep(200);
		}

		if (!stat) throw new Error('Timed out trying to get sync target clock time');

		void this.delete(tempFile); // No need to await for this call

		const endTime = Date.now();
		const expectedTime = Math.round((endTime + startTime) / 2);
		return stat.updated_time - expectedTime;
	}

	// Approximates the current time on the sync target. It caches the time offset to
	// improve performance.
	public async remoteDate() {
		const shouldSyncTime = () => {
			return !this.remoteDateNextCheckTime_ || Date.now() > this.remoteDateNextCheckTime_;
		};

		if (shouldSyncTime()) {
			const release = await this.remoteDateMutex_.acquire();

View on GitHub (pinned to 2654b33620)

Solutions

  1. Usually no action — remoteDate() already catches it and uses the device clock with a 60s retry; timestamp accuracy degrades only slightly.
  2. If timestamp drift causes sync issues, switch to a target with supportsAccurateTimestamp (Joplin Server).
  3. Improve network stability / reduce latency to the target.
  4. Increase the 5000ms loop ceiling in fetchRemoteDateOffset_ if you control the client and the target is known to be slow-but-consistent.

Example fix

// before
while (Date.now() - loopStartTime < 5000) {
  stat = await this.stat(tempFile);
  if (stat) break;
  await time.msleep(200);
}
if (!stat) throw new Error('Timed out trying to get sync target clock time');
// after - longer ceiling for high-lag targets
const CEILING_MS = 15000;
while (Date.now() - loopStartTime < CEILING_MS) {
  stat = await this.stat(tempFile);
  if (stat) break;
  await time.msleep(300);
}
if (!stat) throw new Error('Timed out trying to get sync target clock time');
Defensive patterns

Strategy: fallback

Validate before calling

// Check target reachability before relying on remote clock sync.
const reachable = await fileApi.stat('.');
if (!reachable) throw new Error('Sync target not reachable; cannot sync clock.');

Type guard

function isClockSyncTimeout(e: any): boolean {
  return e && typeof e.message === 'string' && e.message.includes('Timed out trying to get sync target clock time');
}

Try / catch

// remoteDate() already catches this and falls back to device clock.
try {
  const offset = await fileApi.remoteDate();
} catch (e) {
  if (isClockSyncTimeout(e)) { /* acceptable; using device time */ }
  else throw e;
}

Prevention

When it happens

Trigger: During sync startup when the target has eventual consistency (OneDrive, Dropbox) and the just-written temp file isn't readable within 5s; or the target is slow/unreachable mid-operation.

Common situations: Slow cloud target with replication lag (OneDrive, Dropbox, Amazon S3); flaky network causing the put to partially succeed; target rate-limiting reads; very first sync on a high-latency connection. Note: remoteDate() swallows this and defaults to the device clock, so it's non-fatal upstream.

Understand the failure class

Related errors


AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12). Data as JSON: /api/errors/dfa66c3da21699f1. Report an issue: GitHub.