laurent22/joplin · critical · JoplinError

mustUpgradeApp

mustUpgradeApp

Error message

In order to synchronise, please upgrade your application to version %s+

What it means

checkIfCanSync() throws JoplinError with code MustUpgradeApp when the sync target's recorded minimum app version (s.appMinVersion, read from the target's info.json) is greater than the running app version. The only escape hatch is forward compatibility: the target's appMinVersion is exactly '3.7.0' (the Joplin 3.7 format this client understands) and the target has no locked notes (noteLockKey === null). Otherwise the client refuses to sync because a newer client has already written a format it cannot safely read.

Source

Thrown at packages/lib/services/synchronizer/syncInfoUtils.ts:617

	syncInfo.ppk = ppk;
	saveLocalSyncInfo(syncInfo);
}

export function masterKeyById(id: string) {
	return localSyncInfo().masterKeys.find(mk => mk.id === id);
}

export const checkIfCanSync = (s: SyncInfo, appVersion: string) => {
	const isForwardCompatible = () => {
		// Forward compatibility: This version of Joplin supports the Joplin 3.7 sync target format
		if (s.appMinVersion !== forwardCompatibleAppMinVersion) return false;
		// Older Joplin versions don't support sync targets with locked notes
		if (s.noteLockKey !== null) return false;
		return true;
	};

	if (compareVersions(appVersion, s.appMinVersion) < 0 && !isForwardCompatible()) {
		throw new JoplinError(_('In order to synchronise, please upgrade your application to version %s+', s.appMinVersion), ErrorCode.MustUpgradeApp);
	}
};

View on GitHub (pinned to 683240968b)

Solutions

  1. Update Joplin on this device to at least the version named in the message (s.appMinVersion) — this is the intended fix and non-negotiable for a genuinely newer target format.
  2. If all clients are meant to be old, recreate or point to a sync target that was never touched by a newer client (old targets keep their lower appMinVersion).
  3. For app builders embedding this library, call checkIfCanSync(syncInfo, appVersion) up front and show an upgrade prompt instead of letting the error abort mid-sync.
  4. Do not attempt to edit info.json's appMinVersion manually — a newer format may genuinely be present and older clients will corrupt it.

Example fix

// before
try {
	await synchronizer().start();
} catch (error) {
	// generic handler; sync fails cryptically mid-run
}

// after
checkIfCanSync(localSyncInfo(), appVersion); // throws MustUpgradeApp early with a clear message
try {
	await synchronizer().start();
} catch (error) {
	if (error.code === ErrorCode.MustUpgradeApp) {
		return showUpgradePrompt(error.message); // never retry
	}
	throw error;
}
Defensive patterns

Strategy: validation

Validate before calling

import { compareVersions } from '@joplin/lib/utils/compareVersions';

const syncInfo = localSyncInfo();
if (compareVersions(appVersion, syncInfo.appMinVersion) < 0) {
	// Show 'upgrade required' UI before starting sync; do not attempt the sync
}

Try / catch

try {
	await sync();
} catch (error) {
	if (error.code === ErrorCode.MustUpgradeApp) {
		// Unrecoverable by retrying: prompt the user to upgrade the app, then stop
	} else {
		throw error;
	}
}

Prevention

When it happens

Trigger: A device running an older Joplin syncs against a target that a newer Joplin (which bumped appMinVersion in info.json) has already upgraded; downgrading the app after a newer client used the same sync target; a fresh install of an old build pointed at a shared target; the target contains locked notes (noteLockKey set), which kills the forward-compat exception even at appMinVersion 3.7.0.

Common situations: Mixed-version fleets: one device auto-updates to a new release, syncs, and raises the target's minimum version; Linux distro or portable builds pinned to old versions syncing with up-to-date phones; rolling back a client to work around a regression and finding sync locked out; CI with a cached old sync target.

Related errors


AI-assisted analysis of laurent22/joplin@683240968b (2026-08-21). Data as JSON: /api/errors/82fcf0e62f2b1605. Report an issue: GitHub.