laurent22/joplin · error · Error

Remote item %s has an updated_time in the future

Error message

Remote item %s has an updated_time in the future

What it means

A plain Error thrown inside the DELTA loop when a remote item's local copy has updated_time more than a full Day (24h) ahead of the current wall clock. The code comment is explicit: there is no automatic fix. The remote item on the sync target was manually edited (or corrupted) with a future timestamp far beyond now, so every sync loop re-detects the same conflict.

Source

Thrown at packages/lib/Synchronizer.ts:652

						let local = locals[i];
						const ItemClass = BaseItem.itemClass(local);
						const path = BaseItem.systemPath(local);

						// Safety check to avoid infinite loops.
						// - In fact this error is possible if the item is marked for sync (via sync_time or force_sync) while synchronisation is in
						//   progress. When force_sync is not true, this is because the user is typing while the sync is running, so we should continue
						//   looping, as we don't want the sync to stop when there are still un-synced outgoing changes, otherwise this creates a race condition
						//   on mobile, where additional changes made during upload are not synced and don't trigger another sync, whereas a change made immediately
						//   after the sync has finished will trigger another sync. Once the user has stopped typing, it can then break out of the loop and continue
						//   the rest of the process.
						// - It can also happen if the item is directly modified in the sync target, and set with an update_time in the future. In that case,
						//   the local sync_time will be updated to Date.now() but on the next loop it will see that the remote item still has a date ahead
						//   and will see a conflict. There's currently no automatic fix for this - the remote item on the sync target must be fixed manually
						//   (by setting an updated_time less than current time).
						if (donePaths.indexOf(path) >= 0) {
							const syncItem = await BaseItem.syncItem(syncTargetId, local.id, { fields: ['force_sync'] });
							if (local.updated_time > time.unixMs() + Day) {
								throw new Error(sprintf('Remote item %s has an updated_time in the future', path));
							} else if (local.updated_time > time.unixMs()) {
								throw new JoplinError(sprintf('Processing a path that has already been done: %s. Remote item has an updated_time in the future', path), 'processingPathTwice');
							} else if (syncItem.force_sync) {
								throw new JoplinError(sprintf('Processing a path that has already been done: %s. Item was marked for sync using force_sync', path), 'processingPathTwice');
							} else {
								throw new JoplinError(sprintf('Processing a path that has already been done: %s. The user is making changes while the sync is in progress', path), 'changedDuringSync');
							}
						}

						const remote: RemoteItem = result.neverSyncedItemIds.includes(local.id) ? null : await this.apiCall('stat', path);
						let action: SyncAction = null;
						let itemIsReadOnly = false;
						let reason = '';
						let remoteContent = null;

						const getConflictType = (conflictedItem: { type_?: number }) => {
							if (conflictedItem.type_ === BaseModel.TYPE_NOTE) return SyncAction.NoteConflict;
							if (conflictedItem.type_ === BaseModel.TYPE_RESOURCE) return SyncAction.ResourceConflict;

View on GitHub (pinned to 2654b33620)

Solutions

  1. Manually fix the remote item on the sync target so its updated_time is less than the current time (as the code comment instructs).
  2. If the remote target is a filesystem/WebDAV, edit the JSON body of the item and set updated_time to a past value.
  3. Correct the system clock on the device that produced the bad timestamp to prevent recurrence.
  4. As a last resort, delete the offending remote item and let Joplin recreate it from the local copy.
Defensive patterns

Strategy: validation

Validate before calling

// Before syncing, detect items with implausible future timestamps
const Day = 24 * 60 * 60 * 1000;
const now = Date.now();
const bad = items.filter(i => i.updated_time > now + Day);
if (bad.length) {
  // flag for manual fix on the sync target before syncing
  reportFutureTimestampItems(bad);
}

Try / catch

try {
  await synchronizer.start(options);
} catch (error) {
  if (/updated_time in the future/.test(error.message)) {
    // surface the path to the user for manual remote fix
    showManualFixGuidance(error.message);
    return;
  }
  throw error;
}

Prevention

When it happens

Trigger: Encountered when donePaths already contains the path (re-processing) AND local.updated_time exceeds time.unixMs() + Day. This happens after a previous sync pulled down a remote item whose updated_time was set more than 24h in the future — e.g. someone edited the .md file directly on the WebDAV/Joplin Server target and wrote a wrong timestamp.

Common situations: Manual editing of items directly on the sync target (filesystem, WebDAV, S3) with clock skew or hand-edited metadata; a device with a badly wrong system clock previously synced the item.

Related errors


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