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
- Usually no action — remoteDate() already catches it and uses the device clock with a 60s retry; timestamp accuracy degrades only slightly.
- If timestamp drift causes sync issues, switch to a target with supportsAccurateTimestamp (Joplin Server).
- Improve network stability / reduce latency to the target.
- 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
- Use a sync target with supportsAccurateTimestamp (Joplin Server) when timestamp accuracy matters.
- Stabilize network connectivity to the target.
- Accept that eventual-consistency targets may occasionally fail this probe; it's non-fatal.
- Don't block sync on clock offset; the API already falls back.
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- uploadBlob: ${method} ${url}: ${error.toString()}
- AWS S3 bucket not found: ${SyncTargetAmazonS3.s3BucketName()
- Could not access data on server "${options.path()}"
- WebDAV directory not found: ${options.path()}
- lockError
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/dfa66c3da21699f1.
Report an issue: GitHub.